@idds/js 1.0.85 → 1.0.87

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.iife.js +539 -297
  2. package/dist/index.js +539 -297
  3. package/package.json +1 -1
@@ -20,6 +20,7 @@ var InaUI = (() => {
20
20
  // src/js/bundle.js
21
21
  var bundle_exports = {};
22
22
  __export(bundle_exports, {
23
+ Table: () => Table,
23
24
  initAccordion: () => initAccordion,
24
25
  initButtonGroup: () => initButtonGroup,
25
26
  initCheckbox: () => initCheckbox,
@@ -2904,331 +2905,572 @@ var InaUI = (() => {
2904
2905
  }
2905
2906
 
2906
2907
  // src/js/components/stateful/table.js
2907
- function initTable(selectorOrElement, options = {}) {
2908
- const elements = typeof selectorOrElement === "string" ? document.querySelectorAll(selectorOrElement) : selectorOrElement ? selectorOrElement.length ? selectorOrElement : [selectorOrElement] : document.querySelectorAll(`.${PREFIX}-table`);
2909
- elements.forEach((container) => {
2910
- if (container.dataset.initialized === "true") return;
2911
- container.dataset.initialized = "true";
2912
- const {
2913
- columns = [],
2914
- fetchData = null,
2915
- data = [],
2916
- initialPageSize = 10,
2917
- pageSizeOptions = [10, 20, 50],
2918
- initialSortField = null,
2919
- initialSortOrder = null,
2920
- searchContainer = null,
2921
- // External search input element or selector
2922
- searchButton = null,
2923
- // External search button element or selector
2924
- onSearch = null
2925
- // External search handler
2926
- } = options;
2927
- let currentPage = 1;
2928
- let pageSize = initialPageSize;
2929
- let totalPages = 1;
2930
- let total = data.length;
2931
- let sortField = initialSortField;
2932
- let sortOrder = initialSortOrder;
2933
- let searchTerm = "";
2934
- let loading = false;
2935
- let currentData = [...data];
2936
- let tableEl = container.querySelector(`.${PREFIX}-table__container`);
2937
- let theadEl = container.querySelector(`.${PREFIX}-table__header`);
2938
- let tbodyEl = container.querySelector(`.${PREFIX}-table__body`);
2939
- let paginationEl = container.querySelector(`.${PREFIX}-table__pagination`);
2940
- let progressBarEl = container.querySelector(
2941
- `.${PREFIX}-table__progress-bar`
2942
- );
2943
- let progressFillEl = container.querySelector(
2944
- `.${PREFIX}-progress-bar__fill`
2945
- );
2946
- const searchInputEl = typeof searchContainer === "string" ? document.querySelector(searchContainer) : searchContainer;
2947
- const searchBtnEl = typeof searchButton === "string" ? document.querySelector(searchButton) : searchButton;
2948
- if (!tableEl) {
2949
- container.innerHTML = `
2950
- <div class="${PREFIX}-table__progress-bar" style="display: none;">
2951
- <div class="${PREFIX}-progress-bar ${PREFIX}-progress-bar--variant-primary ${PREFIX}-progress-bar--height-sm">
2952
- <div class="${PREFIX}-progress-bar__track">
2953
- <div class="${PREFIX}-progress-bar__fill" style="width: 0%;"></div>
2908
+ var Table = class {
2909
+ constructor(selectorOrElement, options = {}) {
2910
+ this.container = typeof selectorOrElement === "string" ? document.querySelector(selectorOrElement) : selectorOrElement;
2911
+ if (!this.container) {
2912
+ console.warn("[IDDS Table] Container not found:", selectorOrElement);
2913
+ return;
2914
+ }
2915
+ if (this.container.dataset.initialized === "true") {
2916
+ return;
2917
+ }
2918
+ this.container.dataset.initialized = "true";
2919
+ this.options = {
2920
+ columns: [],
2921
+ fetchData: null,
2922
+ data: [],
2923
+ initialPage: 1,
2924
+ initialPageSize: 10,
2925
+ pageSizeOptions: [10, 20, 50],
2926
+ initialSortField: null,
2927
+ initialSortOrder: null,
2928
+ searchPlaceholder: "Input pencarian",
2929
+ buttonSearchLabel: "Cari",
2930
+ selectable: false,
2931
+ onSelectionChange: null,
2932
+ rowKey: "id",
2933
+ showSearch: false,
2934
+ rowClickable: false,
2935
+ onRowClick: null,
2936
+ emptyState: "No data found",
2937
+ showPagination: true,
2938
+ searchContainer: null,
2939
+ searchButton: null,
2940
+ onSearch: null,
2941
+ ...options
2942
+ };
2943
+ this.state = {
2944
+ currentPage: this.options.initialPage,
2945
+ pageSize: this.options.initialPageSize,
2946
+ totalPages: 1,
2947
+ total: this.options.data.length,
2948
+ sortField: this.options.initialSortField,
2949
+ sortOrder: this.options.initialSortOrder,
2950
+ searchTerm: "",
2951
+ loading: false,
2952
+ currentData: [...this.options.data],
2953
+ selectedKeys: /* @__PURE__ */ new Set(),
2954
+ selectedRows: /* @__PURE__ */ new Map()
2955
+ };
2956
+ this.elements = {};
2957
+ this.initDOM();
2958
+ this.bindEvents();
2959
+ this.loadData();
2960
+ }
2961
+ initDOM() {
2962
+ this.container.innerHTML = "";
2963
+ this.container.classList.add(`${PREFIX}-table-wrapper`);
2964
+ if (this.options.showSearch && !this.options.searchContainer) {
2965
+ const searchWrap = document.createElement("div");
2966
+ searchWrap.className = "flex items-center gap-2 mb-4";
2967
+ searchWrap.style.display = "flex";
2968
+ searchWrap.style.alignItems = "center";
2969
+ searchWrap.style.gap = "8px";
2970
+ searchWrap.style.marginBottom = "16px";
2971
+ searchWrap.innerHTML = `
2972
+ <div style="flex: 1;">
2973
+ <div class="${PREFIX}-text-field">
2974
+ <div class="${PREFIX}-text-field__wrapper ${PREFIX}-text-field__wrapper--size-md">
2975
+ <input type="text" class="${PREFIX}-text-field__input" placeholder="${this.options.searchPlaceholder}">
2954
2976
  </div>
2955
2977
  </div>
2956
2978
  </div>
2957
- <table class="${PREFIX}-table__container">
2958
- <thead class="${PREFIX}-table__header"><tr></tr></thead>
2959
- <tbody class="${PREFIX}-table__body"></tbody>
2960
- </table>
2961
- <div class="${PREFIX}-table__pagination"></div>
2979
+ <button type="button" class="${PREFIX}-button ${PREFIX}-button--primary ${PREFIX}-button--md">
2980
+ ${this.options.buttonSearchLabel}
2981
+ </button>
2982
+ `;
2983
+ this.container.appendChild(searchWrap);
2984
+ this.elements.searchInput = searchWrap.querySelector("input");
2985
+ this.elements.searchButton = searchWrap.querySelector("button");
2986
+ } else {
2987
+ this.elements.searchInput = typeof this.options.searchContainer === "string" ? document.querySelector(this.options.searchContainer) : this.options.searchContainer;
2988
+ this.elements.searchButton = typeof this.options.searchButton === "string" ? document.querySelector(this.options.searchButton) : this.options.searchButton;
2989
+ }
2990
+ const tableDiv = document.createElement("div");
2991
+ tableDiv.className = `${PREFIX}-table`;
2992
+ const progressDiv = document.createElement("div");
2993
+ progressDiv.className = `${PREFIX}-table__progress-bar`;
2994
+ progressDiv.style.display = "none";
2995
+ progressDiv.innerHTML = `
2996
+ <div class="${PREFIX}-progress-bar ${PREFIX}-progress-bar--variant-primary ${PREFIX}-progress-bar--height-sm">
2997
+ <div class="${PREFIX}-progress-bar__track">
2998
+ <div class="${PREFIX}-progress-bar__fill" style="width: 0%;"></div>
2999
+ </div>
3000
+ </div>
3001
+ `;
3002
+ tableDiv.appendChild(progressDiv);
3003
+ const tableEl = document.createElement("table");
3004
+ tableEl.className = `${PREFIX}-table__container`;
3005
+ const theadEl = document.createElement("thead");
3006
+ theadEl.className = `${PREFIX}-table__header`;
3007
+ const trHeader = document.createElement("tr");
3008
+ theadEl.appendChild(trHeader);
3009
+ tableEl.appendChild(theadEl);
3010
+ const tbodyEl = document.createElement("tbody");
3011
+ tbodyEl.className = `${PREFIX}-table__body`;
3012
+ tableEl.appendChild(tbodyEl);
3013
+ tableDiv.appendChild(tableEl);
3014
+ const paginationEl = document.createElement("div");
3015
+ paginationEl.className = `${PREFIX}-table__pagination`;
3016
+ if (!this.options.showPagination) {
3017
+ paginationEl.style.display = "none";
3018
+ }
3019
+ tableDiv.appendChild(paginationEl);
3020
+ this.container.appendChild(tableDiv);
3021
+ this.elements.progressEl = progressDiv;
3022
+ this.elements.progressFillEl = progressDiv.querySelector(
3023
+ `.${PREFIX}-progress-bar__fill`
3024
+ );
3025
+ this.elements.theadTr = trHeader;
3026
+ this.elements.tbody = tbodyEl;
3027
+ this.elements.pagination = paginationEl;
3028
+ this.buildHeader();
3029
+ }
3030
+ buildHeader() {
3031
+ this.elements.theadTr.innerHTML = "";
3032
+ if (this.options.selectable) {
3033
+ const th = document.createElement("th");
3034
+ th.className = `${PREFIX}-table__header-cell`;
3035
+ th.style.width = "48px";
3036
+ const checkboxWrap = document.createElement("div");
3037
+ checkboxWrap.className = `${PREFIX}-checkbox`;
3038
+ checkboxWrap.innerHTML = `
3039
+ <label class="${PREFIX}-checkbox__label-wrapper">
3040
+ <input type="checkbox" class="${PREFIX}-checkbox__input" id="selectAll">
3041
+ <div class="${PREFIX}-checkbox__box ${PREFIX}-checkbox__box--unchecked"></div>
3042
+ </label>
2962
3043
  `;
2963
- tableEl = container.querySelector(`.${PREFIX}-table__container`);
2964
- theadEl = container.querySelector(`.${PREFIX}-table__header`);
2965
- tbodyEl = container.querySelector(`.${PREFIX}-table__body`);
2966
- paginationEl = container.querySelector(`.${PREFIX}-table__pagination`);
2967
- progressBarEl = container.querySelector(`.${PREFIX}-table__progress-bar`);
2968
- progressFillEl = container.querySelector(`.${PREFIX}-progress-bar__fill`);
2969
- }
2970
- const trHeader = theadEl.querySelector("tr") || document.createElement("tr");
2971
- if (!theadEl.contains(trHeader)) theadEl.appendChild(trHeader);
2972
- if (columns.length > 0) {
2973
- trHeader.innerHTML = "";
2974
- columns.forEach((col) => {
2975
- const th = document.createElement("th");
2976
- th.className = `${PREFIX}-table__header-cell`;
2977
- if (col.sortable && col.accessor) {
2978
- th.classList.add(`${PREFIX}-table__header-cell--sortable`);
2979
- th.setAttribute("data-sort", col.accessor);
2980
- th.innerHTML = `
2981
- <div class="${PREFIX}-table__sort-controls">
2982
- ${col.header}
2983
- <div class="${PREFIX}-table__sort-icon">
2984
- <div class="${PREFIX}-table__sort-button" data-order="asc">
2985
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9L12 15L18 9"></path></svg>
2986
- </div>
2987
- <div class="${PREFIX}-table__sort-button ${PREFIX}-table__sort-button-right" data-order="desc">
2988
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 15L12 9L6 15"></path></svg>
2989
- </div>
3044
+ th.appendChild(checkboxWrap);
3045
+ this.elements.theadTr.appendChild(th);
3046
+ this.elements.selectAllInput = checkboxWrap.querySelector("input");
3047
+ this.elements.selectAllBox = checkboxWrap.querySelector(
3048
+ `.${PREFIX}-checkbox__box`
3049
+ );
3050
+ this.elements.selectAllInput.addEventListener("change", (e) => {
3051
+ this.toggleAllSelection(e.target.checked);
3052
+ });
3053
+ }
3054
+ this.options.columns.forEach((col) => {
3055
+ const th = document.createElement("th");
3056
+ th.className = `${PREFIX}-table__header-cell`;
3057
+ if (col.sortable && col.accessor) {
3058
+ th.classList.add(`${PREFIX}-table__header-cell--sortable`);
3059
+ th.setAttribute("data-sort", col.accessor);
3060
+ let sortControlsHtml = `
3061
+ <div class="${PREFIX}-table__sort-controls">
3062
+ ${col.header}
3063
+ <div class="${PREFIX}-table__sort-icon">
3064
+ <div class="${PREFIX}-table__sort-button" data-order="asc">
3065
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9L12 15L18 9"></path></svg>
3066
+ </div>
3067
+ <div class="${PREFIX}-table__sort-button ${PREFIX}-table__sort-button-right" data-order="desc">
3068
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 15L12 9L6 15"></path></svg>
2990
3069
  </div>
2991
3070
  </div>
2992
- `;
2993
- th.addEventListener("click", () => {
2994
- const field = col.accessor;
2995
- if (sortField === field) {
2996
- if (sortOrder === "asc") sortOrder = "desc";
2997
- else if (sortOrder === "desc") {
2998
- sortField = null;
2999
- sortOrder = null;
3000
- }
3001
- } else {
3002
- sortField = field;
3003
- sortOrder = "asc";
3071
+ </div>
3072
+ `;
3073
+ th.innerHTML = sortControlsHtml;
3074
+ th.addEventListener("click", () => {
3075
+ const field = col.accessor;
3076
+ if (this.state.sortField === field) {
3077
+ if (this.state.sortOrder === "asc") this.state.sortOrder = "desc";
3078
+ else {
3079
+ this.state.sortField = null;
3080
+ this.state.sortOrder = null;
3004
3081
  }
3005
- currentPage = 1;
3006
- loadData();
3007
- });
3008
- } else {
3009
- th.textContent = col.header;
3082
+ } else {
3083
+ this.state.sortField = field;
3084
+ this.state.sortOrder = "asc";
3085
+ }
3086
+ this.state.currentPage = 1;
3087
+ this.loadData();
3088
+ });
3089
+ } else {
3090
+ th.textContent = col.header;
3091
+ }
3092
+ this.elements.theadTr.appendChild(th);
3093
+ });
3094
+ }
3095
+ bindEvents() {
3096
+ if (this.elements.searchInput && this.elements.searchButton) {
3097
+ const handleSearch = () => {
3098
+ this.state.searchTerm = this.elements.searchInput.value.trim();
3099
+ this.state.currentPage = 1;
3100
+ this.loadData();
3101
+ if (typeof this.options.onSearch === "function") {
3102
+ this.options.onSearch(this.state.searchTerm);
3010
3103
  }
3011
- trHeader.appendChild(th);
3104
+ };
3105
+ this.elements.searchButton.addEventListener("click", handleSearch);
3106
+ this.elements.searchInput.addEventListener("keydown", (e) => {
3107
+ if (e.key === "Enter") handleSearch();
3012
3108
  });
3013
3109
  }
3014
- const sortButtons = theadEl.querySelectorAll(
3015
- `.${PREFIX}-table__sort-button`
3016
- );
3017
- let loadingInterval;
3018
- function setLoading(isLoading) {
3019
- loading = isLoading;
3020
- if (!progressBarEl || !progressFillEl) return;
3021
- if (isLoading) {
3022
- progressBarEl.style.display = "block";
3023
- let progress = 0;
3024
- clearInterval(loadingInterval);
3025
- loadingInterval = setInterval(() => {
3026
- if (progress >= 90 || !loading) {
3027
- clearInterval(loadingInterval);
3028
- if (!loading) {
3029
- progress = 100;
3030
- progressFillEl.style.width = "100%";
3031
- setTimeout(() => {
3032
- progressBarEl.style.display = "none";
3033
- progressFillEl.style.width = "0%";
3034
- }, 300);
3035
- }
3110
+ }
3111
+ setLoading(isLoading) {
3112
+ this.state.loading = isLoading;
3113
+ if (!this.elements.progressEl || !this.elements.progressFillEl) return;
3114
+ if (isLoading) {
3115
+ this.elements.progressEl.style.display = "block";
3116
+ let progress = 0;
3117
+ clearInterval(this.loadingInterval);
3118
+ this.loadingInterval = setInterval(() => {
3119
+ if (progress >= 90 || !this.state.loading) {
3120
+ clearInterval(this.loadingInterval);
3121
+ if (!this.state.loading) {
3122
+ progress = 100;
3123
+ this.elements.progressFillEl.style.width = "100%";
3124
+ setTimeout(() => {
3125
+ this.elements.progressEl.style.display = "none";
3126
+ this.elements.progressFillEl.style.width = "0%";
3127
+ }, 300);
3128
+ }
3129
+ return;
3130
+ }
3131
+ progress += Math.random() * 15;
3132
+ this.elements.progressFillEl.style.width = progress + "%";
3133
+ }, 200);
3134
+ }
3135
+ }
3136
+ renderRows(rowData) {
3137
+ this.elements.tbody.innerHTML = "";
3138
+ if (rowData.length === 0) {
3139
+ const tr = document.createElement("tr");
3140
+ const colspan = this.options.columns.length + (this.options.selectable ? 1 : 0);
3141
+ tr.innerHTML = `<td colspan="${colspan}" class="${PREFIX}-table__empty-cell">${this.options.emptyState}</td>`;
3142
+ this.elements.tbody.appendChild(tr);
3143
+ this.updateSelectAllState();
3144
+ return;
3145
+ }
3146
+ rowData.forEach((row, index) => {
3147
+ const tr = document.createElement("tr");
3148
+ tr.className = `${PREFIX}-table__row`;
3149
+ if (this.options.rowClickable) {
3150
+ tr.style.cursor = "pointer";
3151
+ tr.classList.add(`${PREFIX}-table__row--clickable`);
3152
+ tr.addEventListener("click", (e) => {
3153
+ if (e.target.closest(`.${PREFIX}-checkbox`) && this.options.selectable) {
3036
3154
  return;
3037
3155
  }
3038
- progress += Math.random() * 15;
3039
- progressFillEl.style.width = progress + "%";
3040
- }, 200);
3156
+ if (typeof this.options.onRowClick === "function") {
3157
+ this.options.onRowClick(row, index);
3158
+ }
3159
+ });
3041
3160
  }
3042
- }
3043
- function renderRows(rowData) {
3044
- tbodyEl.innerHTML = "";
3045
- if (rowData.length === 0) {
3046
- const tr = document.createElement("tr");
3047
- tr.innerHTML = `<td colspan="${columns.length || 1}" class="${PREFIX}-table__empty-cell">No data found</td>`;
3048
- tbodyEl.appendChild(tr);
3049
- return;
3161
+ const rowKeyStr = String(row[this.options.rowKey] || index);
3162
+ if (this.options.selectable) {
3163
+ const td = document.createElement("td");
3164
+ td.className = `${PREFIX}-table__cell`;
3165
+ const isChecked = this.state.selectedKeys.has(rowKeyStr);
3166
+ const checkboxWrap = document.createElement("div");
3167
+ checkboxWrap.className = `${PREFIX}-checkbox`;
3168
+ checkboxWrap.innerHTML = `
3169
+ <label class="${PREFIX}-checkbox__label-wrapper">
3170
+ <input type="checkbox" class="${PREFIX}-checkbox__input" ${isChecked ? "checked" : ""}>
3171
+ <div class="${PREFIX}-checkbox__box ${isChecked ? `${PREFIX}-checkbox__box--checked` : `${PREFIX}-checkbox__box--unchecked`}">
3172
+ ${isChecked ? '<svg class="ina-checkbox__icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12l5 5l10 -10"></path></svg>' : ""}
3173
+ </div>
3174
+ </label>
3175
+ `;
3176
+ td.appendChild(checkboxWrap);
3177
+ tr.appendChild(td);
3178
+ const rowInput = checkboxWrap.querySelector("input");
3179
+ rowInput.addEventListener("change", (e) => {
3180
+ this.toggleRowSelection(rowKeyStr, row, e.target.checked);
3181
+ });
3050
3182
  }
3051
- rowData.forEach((row, index) => {
3052
- const tr = document.createElement("tr");
3053
- tr.className = `${PREFIX}-table__row`;
3054
- columns.forEach((col) => {
3055
- const td = document.createElement("td");
3056
- td.className = `${PREFIX}-table__cell`;
3057
- if (typeof col.render === "function") {
3058
- const content = col.render(row, index);
3059
- if (typeof content === "string") {
3060
- td.innerHTML = content;
3061
- } else if (content instanceof HTMLElement) {
3062
- td.appendChild(content);
3063
- } else {
3064
- td.textContent = content;
3065
- }
3183
+ this.options.columns.forEach((col) => {
3184
+ const td = document.createElement("td");
3185
+ td.className = `${PREFIX}-table__cell`;
3186
+ if (typeof col.render === "function") {
3187
+ const content = col.render(row, index);
3188
+ if (typeof content === "string") {
3189
+ td.innerHTML = content;
3190
+ } else if (content instanceof HTMLElement) {
3191
+ td.appendChild(content);
3066
3192
  } else {
3067
- td.textContent = row[col.accessor] !== void 0 ? row[col.accessor] : "";
3193
+ td.textContent = content;
3068
3194
  }
3069
- tr.appendChild(td);
3070
- });
3071
- tbodyEl.appendChild(tr);
3195
+ } else {
3196
+ td.textContent = row[col.accessor] !== void 0 && row[col.accessor] !== null ? row[col.accessor] : "";
3197
+ }
3198
+ tr.appendChild(td);
3072
3199
  });
3073
- }
3074
- function renderPagination() {
3075
- if (!paginationEl) return;
3076
- totalPages = Math.ceil(total / pageSize);
3077
- if (totalPages < 1) totalPages = 1;
3078
- paginationEl.innerHTML = `
3079
- <div class="${PREFIX}-pagination">
3080
- <div class="${PREFIX}-pagination__nav-container">
3081
- <div class="${PREFIX}-pagination__page-info">Halaman ${currentPage} dari ${totalPages}</div>
3082
- <div class="${PREFIX}-pagination__nav-buttons"></div>
3083
- </div>
3084
- <div class="${PREFIX}-pagination__page-size-container">
3085
- <span class="${PREFIX}-pagination__page-size-label">Baris per halaman</span>
3086
- <select class="${PREFIX}-pagination__page-size-select">
3087
- ${pageSizeOptions.map((opt) => `<option value="${opt}" ${pageSize === opt ? "selected" : ""}>${opt}</option>`).join("")}
3088
- </select>
3089
- </div>
3200
+ this.elements.tbody.appendChild(tr);
3201
+ });
3202
+ this.updateSelectAllState();
3203
+ }
3204
+ renderPagination() {
3205
+ if (!this.elements.pagination || !this.options.showPagination) return;
3206
+ this.state.totalPages = Math.ceil(this.state.total / this.state.pageSize);
3207
+ if (this.state.totalPages < 1) this.state.totalPages = 1;
3208
+ this.elements.pagination.innerHTML = `
3209
+ <div class="${PREFIX}-pagination">
3210
+ <div class="${PREFIX}-pagination__nav-container">
3211
+ <div class="${PREFIX}-pagination__page-info">Halaman ${this.state.currentPage} dari ${this.state.totalPages}</div>
3212
+ <div class="${PREFIX}-pagination__nav-buttons"></div>
3090
3213
  </div>
3091
- `;
3092
- const navButtonsContainer = paginationEl.querySelector(
3093
- `.${PREFIX}-pagination__nav-buttons`
3094
- );
3095
- const pageSizeSelect = paginationEl.querySelector(
3096
- `.${PREFIX}-pagination__page-size-select`
3097
- );
3098
- pageSizeSelect.addEventListener("change", (e) => {
3099
- pageSize = parseInt(e.target.value);
3100
- currentPage = 1;
3101
- loadData();
3102
- });
3103
- const createButton = (isNav, enabled, content, onClick) => {
3104
- const btn = document.createElement("button");
3105
- btn.type = "button";
3106
- const baseClass = isNav ? `${PREFIX}-pagination__nav-button` : `${PREFIX}-pagination__page-button`;
3107
- const stateClass = isNav ? enabled ? `${baseClass}--enabled` : `${baseClass}--disabled` : enabled ? `${baseClass}--enabled` : `${baseClass}--active`;
3108
- btn.className = `${baseClass} ${stateClass}`;
3109
- btn.disabled = !enabled;
3110
- if (typeof content === "string") btn.innerHTML = content;
3111
- else btn.appendChild(content);
3112
- btn.onclick = onClick;
3113
- navButtonsContainer.appendChild(btn);
3114
- };
3115
- const iconPaths = {
3116
- first: '<path d="M11 7l-5 5l5 5"></path><path d="M17 7l-5 5l5 5"></path>',
3117
- prev: '<path d="M15 6l-6 6l6 6"></path>',
3118
- next: '<path d="M9 6l6 6l-6 6"></path>',
3119
- last: '<path d="M7 7l5 5l-5 5"></path><path d="M13 7l5 5l-5 5"></path>'
3120
- };
3121
- const getIcon = (type) => `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="${PREFIX}-pagination__nav-icon">${iconPaths[type]}</svg>`;
3122
- createButton(true, currentPage > 1, getIcon("first"), () => {
3123
- currentPage = 1;
3124
- loadData();
3125
- });
3126
- createButton(true, currentPage > 1, getIcon("prev"), () => {
3127
- currentPage--;
3128
- loadData();
3129
- });
3130
- const maxVisible = 5;
3131
- let startPage = Math.max(1, currentPage - Math.floor(maxVisible / 2));
3132
- let endPage = Math.min(totalPages, startPage + maxVisible - 1);
3133
- if (endPage - startPage < maxVisible - 1) {
3134
- startPage = Math.max(1, endPage - maxVisible + 1);
3135
- }
3136
- for (let i = startPage; i <= endPage; i++) {
3137
- createButton(false, i !== currentPage, String(i), () => {
3138
- currentPage = i;
3139
- loadData();
3140
- });
3141
- }
3142
- createButton(true, currentPage < totalPages, getIcon("next"), () => {
3143
- currentPage++;
3144
- loadData();
3145
- });
3146
- createButton(true, currentPage < totalPages, getIcon("last"), () => {
3147
- currentPage = totalPages;
3148
- loadData();
3149
- });
3150
- }
3151
- function updateSortIndicators() {
3152
- sortButtons.forEach((btn) => {
3153
- const headerCell = btn.closest("th");
3154
- if (!headerCell) return;
3155
- const field = headerCell.getAttribute("data-sort");
3156
- const order = btn.getAttribute("data-order");
3157
- const isActive = sortField === field && sortOrder === order;
3158
- btn.classList.toggle(`${PREFIX}-table__sort-button--active`, isActive);
3214
+ <div class="${PREFIX}-pagination__page-size-container">
3215
+ <span class="${PREFIX}-pagination__page-size-label">Baris per halaman</span>
3216
+ <select class="${PREFIX}-pagination__page-size-select">
3217
+ ${this.options.pageSizeOptions.map(
3218
+ (opt) => `<option value="${opt}" ${this.state.pageSize === opt ? "selected" : ""}>${opt}</option>`
3219
+ ).join("")}
3220
+ </select>
3221
+ </div>
3222
+ </div>
3223
+ `;
3224
+ const navButtons = this.elements.pagination.querySelector(
3225
+ `.${PREFIX}-pagination__nav-buttons`
3226
+ );
3227
+ const pageSizeSelect = this.elements.pagination.querySelector(
3228
+ `.${PREFIX}-pagination__page-size-select`
3229
+ );
3230
+ pageSizeSelect.addEventListener("change", (e) => {
3231
+ this.state.pageSize = parseInt(e.target.value, 10);
3232
+ this.state.currentPage = 1;
3233
+ this.loadData();
3234
+ });
3235
+ const createBtn = (isNav, enabled, content, onClick) => {
3236
+ const btn = document.createElement("button");
3237
+ btn.type = "button";
3238
+ const baseCls = isNav ? `${PREFIX}-pagination__nav-button` : `${PREFIX}-pagination__page-button`;
3239
+ const stCls = isNav ? enabled ? `${baseCls}--enabled` : `${baseCls}--disabled` : enabled ? `${baseCls}--enabled` : `${baseCls}--active`;
3240
+ btn.className = `${baseCls} ${stCls}`;
3241
+ btn.disabled = !enabled;
3242
+ btn.innerHTML = content;
3243
+ btn.onclick = onClick;
3244
+ navButtons.appendChild(btn);
3245
+ };
3246
+ const SVG = {
3247
+ first: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="ina-pagination__nav-icon"><path d="M11 7l-5 5l5 5"></path><path d="M17 7l-5 5l5 5"></path></svg>',
3248
+ prev: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="ina-pagination__nav-icon"><path d="M15 6l-6 6l6 6"></path></svg>',
3249
+ next: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="ina-pagination__nav-icon"><path d="M9 6l6 6l-6 6"></path></svg>',
3250
+ last: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="ina-pagination__nav-icon"><path d="M7 7l5 5l-5 5"></path><path d="M13 7l5 5l-5 5"></path></svg>'
3251
+ };
3252
+ createBtn(true, this.state.currentPage > 1, SVG.first, () => {
3253
+ this.state.currentPage = 1;
3254
+ this.loadData();
3255
+ });
3256
+ createBtn(true, this.state.currentPage > 1, SVG.prev, () => {
3257
+ this.state.currentPage--;
3258
+ this.loadData();
3259
+ });
3260
+ const maxVisible = 5;
3261
+ let startPage = Math.max(
3262
+ 1,
3263
+ this.state.currentPage - Math.floor(maxVisible / 2)
3264
+ );
3265
+ let endPage = Math.min(this.state.totalPages, startPage + maxVisible - 1);
3266
+ if (endPage - startPage < maxVisible - 1) {
3267
+ startPage = Math.max(1, endPage - maxVisible + 1);
3268
+ }
3269
+ for (let i = startPage; i <= endPage; i++) {
3270
+ createBtn(false, i !== this.state.currentPage, String(i), () => {
3271
+ this.state.currentPage = i;
3272
+ this.loadData();
3159
3273
  });
3160
3274
  }
3161
- async function loadData() {
3162
- setLoading(true);
3163
- try {
3164
- if (typeof fetchData === "function") {
3165
- const result = await fetchData({
3166
- page: currentPage,
3167
- pageSize,
3168
- sortField,
3169
- sortOrder,
3170
- searchTerm
3275
+ createBtn(
3276
+ true,
3277
+ this.state.currentPage < this.state.totalPages,
3278
+ SVG.next,
3279
+ () => {
3280
+ this.state.currentPage++;
3281
+ this.loadData();
3282
+ }
3283
+ );
3284
+ createBtn(
3285
+ true,
3286
+ this.state.currentPage < this.state.totalPages,
3287
+ SVG.last,
3288
+ () => {
3289
+ this.state.currentPage = this.state.totalPages;
3290
+ this.loadData();
3291
+ }
3292
+ );
3293
+ }
3294
+ updateSortIndicators() {
3295
+ const btns = this.elements.theadTr.querySelectorAll(
3296
+ `.${PREFIX}-table__sort-button`
3297
+ );
3298
+ btns.forEach((btn) => {
3299
+ const th = btn.closest("th");
3300
+ if (!th) return;
3301
+ const field = th.getAttribute("data-sort");
3302
+ const order = btn.getAttribute("data-order");
3303
+ const active = this.state.sortField === field && this.state.sortOrder === order;
3304
+ btn.classList.toggle(`${PREFIX}-table__sort-button--active`, active);
3305
+ });
3306
+ }
3307
+ async loadData() {
3308
+ this.setLoading(true);
3309
+ try {
3310
+ if (typeof this.options.fetchData === "function") {
3311
+ const res = await this.options.fetchData({
3312
+ page: this.state.currentPage,
3313
+ pageSize: this.state.pageSize,
3314
+ sortField: this.state.sortField,
3315
+ sortOrder: this.state.sortOrder,
3316
+ searchTerm: this.state.searchTerm
3317
+ });
3318
+ this.state.total = res.total;
3319
+ this.state.currentData = res.data || [];
3320
+ } else {
3321
+ let filtered = [...this.options.data];
3322
+ if (this.state.searchTerm) {
3323
+ const lower = this.state.searchTerm.toLowerCase();
3324
+ filtered = filtered.filter(
3325
+ (row) => this.options.columns.some(
3326
+ (c) => String(row[c.accessor] || "").toLowerCase().includes(lower)
3327
+ )
3328
+ );
3329
+ }
3330
+ if (this.state.sortField && this.state.sortOrder) {
3331
+ filtered.sort((a, b) => {
3332
+ const aVal = a[this.state.sortField];
3333
+ const bVal = b[this.state.sortField];
3334
+ if (aVal < bVal) return this.state.sortOrder === "asc" ? -1 : 1;
3335
+ if (aVal > bVal) return this.state.sortOrder === "asc" ? 1 : -1;
3336
+ return 0;
3171
3337
  });
3172
- total = result.total;
3173
- currentData = result.data;
3174
- } else {
3175
- let filteredData = [...data];
3176
- if (searchTerm) {
3177
- const lowerSearch = searchTerm.toLowerCase();
3178
- filteredData = filteredData.filter(
3179
- (item) => columns.some(
3180
- (c) => String(item[c.accessor] || "").toLowerCase().includes(lowerSearch)
3181
- )
3182
- );
3183
- }
3184
- if (sortField && sortOrder) {
3185
- filteredData.sort((a, b) => {
3186
- const aVal = a[sortField];
3187
- const bVal = b[sortField];
3188
- if (aVal < bVal) return sortOrder === "asc" ? -1 : 1;
3189
- if (aVal > bVal) return sortOrder === "asc" ? 1 : -1;
3190
- return 0;
3191
- });
3192
- }
3193
- total = filteredData.length;
3194
- const skip = (currentPage - 1) * pageSize;
3195
- currentData = filteredData.slice(skip, skip + pageSize);
3196
3338
  }
3197
- renderRows(currentData);
3198
- renderPagination();
3199
- updateSortIndicators();
3200
- } catch (error) {
3201
- console.error("Table API Error:", error);
3202
- } finally {
3203
- setLoading(false);
3339
+ this.state.total = filtered.length;
3340
+ const skip = (this.state.currentPage - 1) * this.state.pageSize;
3341
+ this.state.currentData = filtered.slice(
3342
+ skip,
3343
+ skip + this.state.pageSize
3344
+ );
3204
3345
  }
3346
+ this.renderRows(this.state.currentData);
3347
+ this.renderPagination();
3348
+ this.updateSortIndicators();
3349
+ } catch (e) {
3350
+ console.error("Table API Error:", e);
3351
+ } finally {
3352
+ this.setLoading(false);
3205
3353
  }
3206
- if (searchInputEl && searchBtnEl) {
3207
- const handleSearch = () => {
3208
- searchTerm = searchInputEl.value.trim();
3209
- currentPage = 1;
3210
- loadData();
3211
- if (typeof onSearch === "function") onSearch(searchTerm);
3212
- };
3213
- searchBtnEl.addEventListener("click", handleSearch);
3214
- searchInputEl.addEventListener("keydown", (e) => {
3215
- if (e.key === "Enter") handleSearch();
3216
- });
3354
+ }
3355
+ toggleRowSelection(keyStr, row, checked) {
3356
+ if (checked) {
3357
+ this.state.selectedKeys.add(keyStr);
3358
+ this.state.selectedRows.set(keyStr, row);
3359
+ } else {
3360
+ this.state.selectedKeys.delete(keyStr);
3361
+ this.state.selectedRows.delete(keyStr);
3217
3362
  }
3218
- loadData();
3219
- container.__tableAPI = {
3220
- reload: loadData,
3221
- setSearchTerm: (term) => {
3222
- searchTerm = term;
3223
- currentPage = 1;
3224
- loadData();
3225
- },
3226
- setPage: (page) => {
3227
- currentPage = page;
3228
- loadData();
3363
+ const inputs = this.elements.tbody.querySelectorAll(
3364
+ `.${PREFIX}-checkbox__input`
3365
+ );
3366
+ inputs.forEach((input) => {
3367
+ const box = input.nextElementSibling;
3368
+ if (input.checked) {
3369
+ box.classList.remove(`${PREFIX}-checkbox__box--unchecked`);
3370
+ box.classList.add(`${PREFIX}-checkbox__box--checked`);
3371
+ box.innerHTML = '<svg class="ina-checkbox__icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12l5 5l10 -10"></path></svg>';
3372
+ } else {
3373
+ box.classList.remove(`${PREFIX}-checkbox__box--checked`);
3374
+ box.classList.add(`${PREFIX}-checkbox__box--unchecked`);
3375
+ box.innerHTML = "";
3376
+ }
3377
+ });
3378
+ this.updateSelectAllState();
3379
+ this.triggerSelectionChange();
3380
+ }
3381
+ toggleAllSelection(checked) {
3382
+ this.state.currentData.forEach((row, index) => {
3383
+ const keyStr = String(row[this.options.rowKey] || index);
3384
+ if (checked) {
3385
+ this.state.selectedKeys.add(keyStr);
3386
+ this.state.selectedRows.set(keyStr, row);
3387
+ } else {
3388
+ this.state.selectedKeys.delete(keyStr);
3389
+ this.state.selectedRows.delete(keyStr);
3229
3390
  }
3391
+ });
3392
+ this.renderRows(this.state.currentData);
3393
+ this.triggerSelectionChange();
3394
+ }
3395
+ updateSelectAllState() {
3396
+ if (!this.options.selectable || !this.elements.selectAllInput) return;
3397
+ const checkboxes = this.elements.tbody.querySelectorAll(
3398
+ 'input[type="checkbox"]'
3399
+ );
3400
+ const checkedCount = Array.from(checkboxes).filter(
3401
+ (cb) => cb.checked
3402
+ ).length;
3403
+ const totalCount = this.state.currentData.length;
3404
+ const box = this.elements.selectAllBox;
3405
+ box.innerHTML = "";
3406
+ if (totalCount === 0 || checkedCount === 0) {
3407
+ this.elements.selectAllInput.checked = false;
3408
+ this.elements.selectAllInput.indeterminate = false;
3409
+ box.classList.remove(
3410
+ `${PREFIX}-checkbox__box--checked`,
3411
+ `${PREFIX}-checkbox__box--indeterminate`
3412
+ );
3413
+ box.classList.add(`${PREFIX}-checkbox__box--unchecked`);
3414
+ } else if (checkedCount === totalCount) {
3415
+ this.elements.selectAllInput.checked = true;
3416
+ this.elements.selectAllInput.indeterminate = false;
3417
+ box.classList.remove(
3418
+ `${PREFIX}-checkbox__box--unchecked`,
3419
+ `${PREFIX}-checkbox__box--indeterminate`
3420
+ );
3421
+ box.classList.add(`${PREFIX}-checkbox__box--checked`);
3422
+ box.innerHTML = '<svg class="ina-checkbox__icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12l5 5l10 -10"></path></svg>';
3423
+ } else {
3424
+ this.elements.selectAllInput.checked = false;
3425
+ this.elements.selectAllInput.indeterminate = true;
3426
+ box.classList.remove(
3427
+ `${PREFIX}-checkbox__box--unchecked`,
3428
+ `${PREFIX}-checkbox__box--checked`
3429
+ );
3430
+ box.classList.add(`${PREFIX}-checkbox__box--indeterminate`);
3431
+ box.innerHTML = '<svg class="ina-checkbox__icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"></path></svg>';
3432
+ }
3433
+ }
3434
+ triggerSelectionChange() {
3435
+ if (typeof this.options.onSelectionChange === "function") {
3436
+ const keys = Array.from(this.state.selectedKeys);
3437
+ const rows = Array.from(this.state.selectedRows.values());
3438
+ this.options.onSelectionChange(keys, rows);
3439
+ }
3440
+ }
3441
+ // API Methods
3442
+ reload() {
3443
+ this.loadData();
3444
+ }
3445
+ setSearchTerm(term) {
3446
+ this.state.searchTerm = term;
3447
+ this.state.currentPage = 1;
3448
+ if (this.elements.searchInput) {
3449
+ this.elements.searchInput.value = term;
3450
+ }
3451
+ this.loadData();
3452
+ }
3453
+ setPage(page) {
3454
+ this.state.currentPage = page;
3455
+ this.loadData();
3456
+ }
3457
+ getSelectedRows() {
3458
+ return {
3459
+ keys: Array.from(this.state.selectedKeys),
3460
+ rows: Array.from(this.state.selectedRows.values())
3230
3461
  };
3462
+ }
3463
+ };
3464
+ function initTable(selectorOrElement, options = {}) {
3465
+ const elements = typeof selectorOrElement === "string" ? document.querySelectorAll(selectorOrElement) : selectorOrElement ? typeof selectorOrElement.length !== "undefined" ? selectorOrElement : [selectorOrElement] : document.querySelectorAll(`.${PREFIX}-table`);
3466
+ const instances = [];
3467
+ elements.forEach((container) => {
3468
+ const instance = new Table(container, options);
3469
+ container.__tableAPI = instance;
3470
+ instances.push(instance);
3231
3471
  });
3472
+ if (instances.length === 0) return null;
3473
+ return instances.length === 1 ? instances[0] : instances;
3232
3474
  }
3233
3475
 
3234
3476
  // src/js/components/stateless/toast.js