@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
package/dist/index.js CHANGED
@@ -2989,331 +2989,572 @@ function initTabHorizontal() {
2989
2989
  }
2990
2990
 
2991
2991
  // src/js/components/stateful/table.js
2992
- function initTable(selectorOrElement, options = {}) {
2993
- const elements = typeof selectorOrElement === "string" ? document.querySelectorAll(selectorOrElement) : selectorOrElement ? selectorOrElement.length ? selectorOrElement : [selectorOrElement] : document.querySelectorAll(`.${PREFIX}-table`);
2994
- elements.forEach((container) => {
2995
- if (container.dataset.initialized === "true") return;
2996
- container.dataset.initialized = "true";
2997
- const {
2998
- columns = [],
2999
- fetchData = null,
3000
- data = [],
3001
- initialPageSize = 10,
3002
- pageSizeOptions = [10, 20, 50],
3003
- initialSortField = null,
3004
- initialSortOrder = null,
3005
- searchContainer = null,
3006
- // External search input element or selector
3007
- searchButton = null,
3008
- // External search button element or selector
3009
- onSearch = null
3010
- // External search handler
3011
- } = options;
3012
- let currentPage = 1;
3013
- let pageSize = initialPageSize;
3014
- let totalPages = 1;
3015
- let total = data.length;
3016
- let sortField = initialSortField;
3017
- let sortOrder = initialSortOrder;
3018
- let searchTerm = "";
3019
- let loading = false;
3020
- let currentData = [...data];
3021
- let tableEl = container.querySelector(`.${PREFIX}-table__container`);
3022
- let theadEl = container.querySelector(`.${PREFIX}-table__header`);
3023
- let tbodyEl = container.querySelector(`.${PREFIX}-table__body`);
3024
- let paginationEl = container.querySelector(`.${PREFIX}-table__pagination`);
3025
- let progressBarEl = container.querySelector(
3026
- `.${PREFIX}-table__progress-bar`
3027
- );
3028
- let progressFillEl = container.querySelector(
3029
- `.${PREFIX}-progress-bar__fill`
3030
- );
3031
- const searchInputEl = typeof searchContainer === "string" ? document.querySelector(searchContainer) : searchContainer;
3032
- const searchBtnEl = typeof searchButton === "string" ? document.querySelector(searchButton) : searchButton;
3033
- if (!tableEl) {
3034
- container.innerHTML = `
3035
- <div class="${PREFIX}-table__progress-bar" style="display: none;">
3036
- <div class="${PREFIX}-progress-bar ${PREFIX}-progress-bar--variant-primary ${PREFIX}-progress-bar--height-sm">
3037
- <div class="${PREFIX}-progress-bar__track">
3038
- <div class="${PREFIX}-progress-bar__fill" style="width: 0%;"></div>
2992
+ var Table = class {
2993
+ constructor(selectorOrElement, options = {}) {
2994
+ this.container = typeof selectorOrElement === "string" ? document.querySelector(selectorOrElement) : selectorOrElement;
2995
+ if (!this.container) {
2996
+ console.warn("[IDDS Table] Container not found:", selectorOrElement);
2997
+ return;
2998
+ }
2999
+ if (this.container.dataset.initialized === "true") {
3000
+ return;
3001
+ }
3002
+ this.container.dataset.initialized = "true";
3003
+ this.options = {
3004
+ columns: [],
3005
+ fetchData: null,
3006
+ data: [],
3007
+ initialPage: 1,
3008
+ initialPageSize: 10,
3009
+ pageSizeOptions: [10, 20, 50],
3010
+ initialSortField: null,
3011
+ initialSortOrder: null,
3012
+ searchPlaceholder: "Input pencarian",
3013
+ buttonSearchLabel: "Cari",
3014
+ selectable: false,
3015
+ onSelectionChange: null,
3016
+ rowKey: "id",
3017
+ showSearch: false,
3018
+ rowClickable: false,
3019
+ onRowClick: null,
3020
+ emptyState: "No data found",
3021
+ showPagination: true,
3022
+ searchContainer: null,
3023
+ searchButton: null,
3024
+ onSearch: null,
3025
+ ...options
3026
+ };
3027
+ this.state = {
3028
+ currentPage: this.options.initialPage,
3029
+ pageSize: this.options.initialPageSize,
3030
+ totalPages: 1,
3031
+ total: this.options.data.length,
3032
+ sortField: this.options.initialSortField,
3033
+ sortOrder: this.options.initialSortOrder,
3034
+ searchTerm: "",
3035
+ loading: false,
3036
+ currentData: [...this.options.data],
3037
+ selectedKeys: /* @__PURE__ */ new Set(),
3038
+ selectedRows: /* @__PURE__ */ new Map()
3039
+ };
3040
+ this.elements = {};
3041
+ this.initDOM();
3042
+ this.bindEvents();
3043
+ this.loadData();
3044
+ }
3045
+ initDOM() {
3046
+ this.container.innerHTML = "";
3047
+ this.container.classList.add(`${PREFIX}-table-wrapper`);
3048
+ if (this.options.showSearch && !this.options.searchContainer) {
3049
+ const searchWrap = document.createElement("div");
3050
+ searchWrap.className = "flex items-center gap-2 mb-4";
3051
+ searchWrap.style.display = "flex";
3052
+ searchWrap.style.alignItems = "center";
3053
+ searchWrap.style.gap = "8px";
3054
+ searchWrap.style.marginBottom = "16px";
3055
+ searchWrap.innerHTML = `
3056
+ <div style="flex: 1;">
3057
+ <div class="${PREFIX}-text-field">
3058
+ <div class="${PREFIX}-text-field__wrapper ${PREFIX}-text-field__wrapper--size-md">
3059
+ <input type="text" class="${PREFIX}-text-field__input" placeholder="${this.options.searchPlaceholder}">
3039
3060
  </div>
3040
3061
  </div>
3041
3062
  </div>
3042
- <table class="${PREFIX}-table__container">
3043
- <thead class="${PREFIX}-table__header"><tr></tr></thead>
3044
- <tbody class="${PREFIX}-table__body"></tbody>
3045
- </table>
3046
- <div class="${PREFIX}-table__pagination"></div>
3063
+ <button type="button" class="${PREFIX}-button ${PREFIX}-button--primary ${PREFIX}-button--md">
3064
+ ${this.options.buttonSearchLabel}
3065
+ </button>
3066
+ `;
3067
+ this.container.appendChild(searchWrap);
3068
+ this.elements.searchInput = searchWrap.querySelector("input");
3069
+ this.elements.searchButton = searchWrap.querySelector("button");
3070
+ } else {
3071
+ this.elements.searchInput = typeof this.options.searchContainer === "string" ? document.querySelector(this.options.searchContainer) : this.options.searchContainer;
3072
+ this.elements.searchButton = typeof this.options.searchButton === "string" ? document.querySelector(this.options.searchButton) : this.options.searchButton;
3073
+ }
3074
+ const tableDiv = document.createElement("div");
3075
+ tableDiv.className = `${PREFIX}-table`;
3076
+ const progressDiv = document.createElement("div");
3077
+ progressDiv.className = `${PREFIX}-table__progress-bar`;
3078
+ progressDiv.style.display = "none";
3079
+ progressDiv.innerHTML = `
3080
+ <div class="${PREFIX}-progress-bar ${PREFIX}-progress-bar--variant-primary ${PREFIX}-progress-bar--height-sm">
3081
+ <div class="${PREFIX}-progress-bar__track">
3082
+ <div class="${PREFIX}-progress-bar__fill" style="width: 0%;"></div>
3083
+ </div>
3084
+ </div>
3085
+ `;
3086
+ tableDiv.appendChild(progressDiv);
3087
+ const tableEl = document.createElement("table");
3088
+ tableEl.className = `${PREFIX}-table__container`;
3089
+ const theadEl = document.createElement("thead");
3090
+ theadEl.className = `${PREFIX}-table__header`;
3091
+ const trHeader = document.createElement("tr");
3092
+ theadEl.appendChild(trHeader);
3093
+ tableEl.appendChild(theadEl);
3094
+ const tbodyEl = document.createElement("tbody");
3095
+ tbodyEl.className = `${PREFIX}-table__body`;
3096
+ tableEl.appendChild(tbodyEl);
3097
+ tableDiv.appendChild(tableEl);
3098
+ const paginationEl = document.createElement("div");
3099
+ paginationEl.className = `${PREFIX}-table__pagination`;
3100
+ if (!this.options.showPagination) {
3101
+ paginationEl.style.display = "none";
3102
+ }
3103
+ tableDiv.appendChild(paginationEl);
3104
+ this.container.appendChild(tableDiv);
3105
+ this.elements.progressEl = progressDiv;
3106
+ this.elements.progressFillEl = progressDiv.querySelector(
3107
+ `.${PREFIX}-progress-bar__fill`
3108
+ );
3109
+ this.elements.theadTr = trHeader;
3110
+ this.elements.tbody = tbodyEl;
3111
+ this.elements.pagination = paginationEl;
3112
+ this.buildHeader();
3113
+ }
3114
+ buildHeader() {
3115
+ this.elements.theadTr.innerHTML = "";
3116
+ if (this.options.selectable) {
3117
+ const th = document.createElement("th");
3118
+ th.className = `${PREFIX}-table__header-cell`;
3119
+ th.style.width = "48px";
3120
+ const checkboxWrap = document.createElement("div");
3121
+ checkboxWrap.className = `${PREFIX}-checkbox`;
3122
+ checkboxWrap.innerHTML = `
3123
+ <label class="${PREFIX}-checkbox__label-wrapper">
3124
+ <input type="checkbox" class="${PREFIX}-checkbox__input" id="selectAll">
3125
+ <div class="${PREFIX}-checkbox__box ${PREFIX}-checkbox__box--unchecked"></div>
3126
+ </label>
3047
3127
  `;
3048
- tableEl = container.querySelector(`.${PREFIX}-table__container`);
3049
- theadEl = container.querySelector(`.${PREFIX}-table__header`);
3050
- tbodyEl = container.querySelector(`.${PREFIX}-table__body`);
3051
- paginationEl = container.querySelector(`.${PREFIX}-table__pagination`);
3052
- progressBarEl = container.querySelector(`.${PREFIX}-table__progress-bar`);
3053
- progressFillEl = container.querySelector(`.${PREFIX}-progress-bar__fill`);
3054
- }
3055
- const trHeader = theadEl.querySelector("tr") || document.createElement("tr");
3056
- if (!theadEl.contains(trHeader)) theadEl.appendChild(trHeader);
3057
- if (columns.length > 0) {
3058
- trHeader.innerHTML = "";
3059
- columns.forEach((col) => {
3060
- const th = document.createElement("th");
3061
- th.className = `${PREFIX}-table__header-cell`;
3062
- if (col.sortable && col.accessor) {
3063
- th.classList.add(`${PREFIX}-table__header-cell--sortable`);
3064
- th.setAttribute("data-sort", col.accessor);
3065
- th.innerHTML = `
3066
- <div class="${PREFIX}-table__sort-controls">
3067
- ${col.header}
3068
- <div class="${PREFIX}-table__sort-icon">
3069
- <div class="${PREFIX}-table__sort-button" data-order="asc">
3070
- <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>
3071
- </div>
3072
- <div class="${PREFIX}-table__sort-button ${PREFIX}-table__sort-button-right" data-order="desc">
3073
- <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>
3074
- </div>
3128
+ th.appendChild(checkboxWrap);
3129
+ this.elements.theadTr.appendChild(th);
3130
+ this.elements.selectAllInput = checkboxWrap.querySelector("input");
3131
+ this.elements.selectAllBox = checkboxWrap.querySelector(
3132
+ `.${PREFIX}-checkbox__box`
3133
+ );
3134
+ this.elements.selectAllInput.addEventListener("change", (e) => {
3135
+ this.toggleAllSelection(e.target.checked);
3136
+ });
3137
+ }
3138
+ this.options.columns.forEach((col) => {
3139
+ const th = document.createElement("th");
3140
+ th.className = `${PREFIX}-table__header-cell`;
3141
+ if (col.sortable && col.accessor) {
3142
+ th.classList.add(`${PREFIX}-table__header-cell--sortable`);
3143
+ th.setAttribute("data-sort", col.accessor);
3144
+ let sortControlsHtml = `
3145
+ <div class="${PREFIX}-table__sort-controls">
3146
+ ${col.header}
3147
+ <div class="${PREFIX}-table__sort-icon">
3148
+ <div class="${PREFIX}-table__sort-button" data-order="asc">
3149
+ <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>
3150
+ </div>
3151
+ <div class="${PREFIX}-table__sort-button ${PREFIX}-table__sort-button-right" data-order="desc">
3152
+ <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>
3075
3153
  </div>
3076
3154
  </div>
3077
- `;
3078
- th.addEventListener("click", () => {
3079
- const field = col.accessor;
3080
- if (sortField === field) {
3081
- if (sortOrder === "asc") sortOrder = "desc";
3082
- else if (sortOrder === "desc") {
3083
- sortField = null;
3084
- sortOrder = null;
3085
- }
3086
- } else {
3087
- sortField = field;
3088
- sortOrder = "asc";
3155
+ </div>
3156
+ `;
3157
+ th.innerHTML = sortControlsHtml;
3158
+ th.addEventListener("click", () => {
3159
+ const field = col.accessor;
3160
+ if (this.state.sortField === field) {
3161
+ if (this.state.sortOrder === "asc") this.state.sortOrder = "desc";
3162
+ else {
3163
+ this.state.sortField = null;
3164
+ this.state.sortOrder = null;
3089
3165
  }
3090
- currentPage = 1;
3091
- loadData();
3092
- });
3093
- } else {
3094
- th.textContent = col.header;
3166
+ } else {
3167
+ this.state.sortField = field;
3168
+ this.state.sortOrder = "asc";
3169
+ }
3170
+ this.state.currentPage = 1;
3171
+ this.loadData();
3172
+ });
3173
+ } else {
3174
+ th.textContent = col.header;
3175
+ }
3176
+ this.elements.theadTr.appendChild(th);
3177
+ });
3178
+ }
3179
+ bindEvents() {
3180
+ if (this.elements.searchInput && this.elements.searchButton) {
3181
+ const handleSearch = () => {
3182
+ this.state.searchTerm = this.elements.searchInput.value.trim();
3183
+ this.state.currentPage = 1;
3184
+ this.loadData();
3185
+ if (typeof this.options.onSearch === "function") {
3186
+ this.options.onSearch(this.state.searchTerm);
3095
3187
  }
3096
- trHeader.appendChild(th);
3188
+ };
3189
+ this.elements.searchButton.addEventListener("click", handleSearch);
3190
+ this.elements.searchInput.addEventListener("keydown", (e) => {
3191
+ if (e.key === "Enter") handleSearch();
3097
3192
  });
3098
3193
  }
3099
- const sortButtons = theadEl.querySelectorAll(
3100
- `.${PREFIX}-table__sort-button`
3101
- );
3102
- let loadingInterval;
3103
- function setLoading(isLoading) {
3104
- loading = isLoading;
3105
- if (!progressBarEl || !progressFillEl) return;
3106
- if (isLoading) {
3107
- progressBarEl.style.display = "block";
3108
- let progress = 0;
3109
- clearInterval(loadingInterval);
3110
- loadingInterval = setInterval(() => {
3111
- if (progress >= 90 || !loading) {
3112
- clearInterval(loadingInterval);
3113
- if (!loading) {
3114
- progress = 100;
3115
- progressFillEl.style.width = "100%";
3116
- setTimeout(() => {
3117
- progressBarEl.style.display = "none";
3118
- progressFillEl.style.width = "0%";
3119
- }, 300);
3120
- }
3194
+ }
3195
+ setLoading(isLoading) {
3196
+ this.state.loading = isLoading;
3197
+ if (!this.elements.progressEl || !this.elements.progressFillEl) return;
3198
+ if (isLoading) {
3199
+ this.elements.progressEl.style.display = "block";
3200
+ let progress = 0;
3201
+ clearInterval(this.loadingInterval);
3202
+ this.loadingInterval = setInterval(() => {
3203
+ if (progress >= 90 || !this.state.loading) {
3204
+ clearInterval(this.loadingInterval);
3205
+ if (!this.state.loading) {
3206
+ progress = 100;
3207
+ this.elements.progressFillEl.style.width = "100%";
3208
+ setTimeout(() => {
3209
+ this.elements.progressEl.style.display = "none";
3210
+ this.elements.progressFillEl.style.width = "0%";
3211
+ }, 300);
3212
+ }
3213
+ return;
3214
+ }
3215
+ progress += Math.random() * 15;
3216
+ this.elements.progressFillEl.style.width = progress + "%";
3217
+ }, 200);
3218
+ }
3219
+ }
3220
+ renderRows(rowData) {
3221
+ this.elements.tbody.innerHTML = "";
3222
+ if (rowData.length === 0) {
3223
+ const tr = document.createElement("tr");
3224
+ const colspan = this.options.columns.length + (this.options.selectable ? 1 : 0);
3225
+ tr.innerHTML = `<td colspan="${colspan}" class="${PREFIX}-table__empty-cell">${this.options.emptyState}</td>`;
3226
+ this.elements.tbody.appendChild(tr);
3227
+ this.updateSelectAllState();
3228
+ return;
3229
+ }
3230
+ rowData.forEach((row, index) => {
3231
+ const tr = document.createElement("tr");
3232
+ tr.className = `${PREFIX}-table__row`;
3233
+ if (this.options.rowClickable) {
3234
+ tr.style.cursor = "pointer";
3235
+ tr.classList.add(`${PREFIX}-table__row--clickable`);
3236
+ tr.addEventListener("click", (e) => {
3237
+ if (e.target.closest(`.${PREFIX}-checkbox`) && this.options.selectable) {
3121
3238
  return;
3122
3239
  }
3123
- progress += Math.random() * 15;
3124
- progressFillEl.style.width = progress + "%";
3125
- }, 200);
3240
+ if (typeof this.options.onRowClick === "function") {
3241
+ this.options.onRowClick(row, index);
3242
+ }
3243
+ });
3126
3244
  }
3127
- }
3128
- function renderRows(rowData) {
3129
- tbodyEl.innerHTML = "";
3130
- if (rowData.length === 0) {
3131
- const tr = document.createElement("tr");
3132
- tr.innerHTML = `<td colspan="${columns.length || 1}" class="${PREFIX}-table__empty-cell">No data found</td>`;
3133
- tbodyEl.appendChild(tr);
3134
- return;
3245
+ const rowKeyStr = String(row[this.options.rowKey] || index);
3246
+ if (this.options.selectable) {
3247
+ const td = document.createElement("td");
3248
+ td.className = `${PREFIX}-table__cell`;
3249
+ const isChecked = this.state.selectedKeys.has(rowKeyStr);
3250
+ const checkboxWrap = document.createElement("div");
3251
+ checkboxWrap.className = `${PREFIX}-checkbox`;
3252
+ checkboxWrap.innerHTML = `
3253
+ <label class="${PREFIX}-checkbox__label-wrapper">
3254
+ <input type="checkbox" class="${PREFIX}-checkbox__input" ${isChecked ? "checked" : ""}>
3255
+ <div class="${PREFIX}-checkbox__box ${isChecked ? `${PREFIX}-checkbox__box--checked` : `${PREFIX}-checkbox__box--unchecked`}">
3256
+ ${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>' : ""}
3257
+ </div>
3258
+ </label>
3259
+ `;
3260
+ td.appendChild(checkboxWrap);
3261
+ tr.appendChild(td);
3262
+ const rowInput = checkboxWrap.querySelector("input");
3263
+ rowInput.addEventListener("change", (e) => {
3264
+ this.toggleRowSelection(rowKeyStr, row, e.target.checked);
3265
+ });
3135
3266
  }
3136
- rowData.forEach((row, index) => {
3137
- const tr = document.createElement("tr");
3138
- tr.className = `${PREFIX}-table__row`;
3139
- columns.forEach((col) => {
3140
- const td = document.createElement("td");
3141
- td.className = `${PREFIX}-table__cell`;
3142
- if (typeof col.render === "function") {
3143
- const content = col.render(row, index);
3144
- if (typeof content === "string") {
3145
- td.innerHTML = content;
3146
- } else if (content instanceof HTMLElement) {
3147
- td.appendChild(content);
3148
- } else {
3149
- td.textContent = content;
3150
- }
3267
+ this.options.columns.forEach((col) => {
3268
+ const td = document.createElement("td");
3269
+ td.className = `${PREFIX}-table__cell`;
3270
+ if (typeof col.render === "function") {
3271
+ const content = col.render(row, index);
3272
+ if (typeof content === "string") {
3273
+ td.innerHTML = content;
3274
+ } else if (content instanceof HTMLElement) {
3275
+ td.appendChild(content);
3151
3276
  } else {
3152
- td.textContent = row[col.accessor] !== void 0 ? row[col.accessor] : "";
3277
+ td.textContent = content;
3153
3278
  }
3154
- tr.appendChild(td);
3155
- });
3156
- tbodyEl.appendChild(tr);
3279
+ } else {
3280
+ td.textContent = row[col.accessor] !== void 0 && row[col.accessor] !== null ? row[col.accessor] : "";
3281
+ }
3282
+ tr.appendChild(td);
3157
3283
  });
3158
- }
3159
- function renderPagination() {
3160
- if (!paginationEl) return;
3161
- totalPages = Math.ceil(total / pageSize);
3162
- if (totalPages < 1) totalPages = 1;
3163
- paginationEl.innerHTML = `
3164
- <div class="${PREFIX}-pagination">
3165
- <div class="${PREFIX}-pagination__nav-container">
3166
- <div class="${PREFIX}-pagination__page-info">Halaman ${currentPage} dari ${totalPages}</div>
3167
- <div class="${PREFIX}-pagination__nav-buttons"></div>
3168
- </div>
3169
- <div class="${PREFIX}-pagination__page-size-container">
3170
- <span class="${PREFIX}-pagination__page-size-label">Baris per halaman</span>
3171
- <select class="${PREFIX}-pagination__page-size-select">
3172
- ${pageSizeOptions.map((opt) => `<option value="${opt}" ${pageSize === opt ? "selected" : ""}>${opt}</option>`).join("")}
3173
- </select>
3174
- </div>
3284
+ this.elements.tbody.appendChild(tr);
3285
+ });
3286
+ this.updateSelectAllState();
3287
+ }
3288
+ renderPagination() {
3289
+ if (!this.elements.pagination || !this.options.showPagination) return;
3290
+ this.state.totalPages = Math.ceil(this.state.total / this.state.pageSize);
3291
+ if (this.state.totalPages < 1) this.state.totalPages = 1;
3292
+ this.elements.pagination.innerHTML = `
3293
+ <div class="${PREFIX}-pagination">
3294
+ <div class="${PREFIX}-pagination__nav-container">
3295
+ <div class="${PREFIX}-pagination__page-info">Halaman ${this.state.currentPage} dari ${this.state.totalPages}</div>
3296
+ <div class="${PREFIX}-pagination__nav-buttons"></div>
3175
3297
  </div>
3176
- `;
3177
- const navButtonsContainer = paginationEl.querySelector(
3178
- `.${PREFIX}-pagination__nav-buttons`
3179
- );
3180
- const pageSizeSelect = paginationEl.querySelector(
3181
- `.${PREFIX}-pagination__page-size-select`
3182
- );
3183
- pageSizeSelect.addEventListener("change", (e) => {
3184
- pageSize = parseInt(e.target.value);
3185
- currentPage = 1;
3186
- loadData();
3187
- });
3188
- const createButton = (isNav, enabled, content, onClick) => {
3189
- const btn = document.createElement("button");
3190
- btn.type = "button";
3191
- const baseClass = isNav ? `${PREFIX}-pagination__nav-button` : `${PREFIX}-pagination__page-button`;
3192
- const stateClass = isNav ? enabled ? `${baseClass}--enabled` : `${baseClass}--disabled` : enabled ? `${baseClass}--enabled` : `${baseClass}--active`;
3193
- btn.className = `${baseClass} ${stateClass}`;
3194
- btn.disabled = !enabled;
3195
- if (typeof content === "string") btn.innerHTML = content;
3196
- else btn.appendChild(content);
3197
- btn.onclick = onClick;
3198
- navButtonsContainer.appendChild(btn);
3199
- };
3200
- const iconPaths = {
3201
- first: '<path d="M11 7l-5 5l5 5"></path><path d="M17 7l-5 5l5 5"></path>',
3202
- prev: '<path d="M15 6l-6 6l6 6"></path>',
3203
- next: '<path d="M9 6l6 6l-6 6"></path>',
3204
- last: '<path d="M7 7l5 5l-5 5"></path><path d="M13 7l5 5l-5 5"></path>'
3205
- };
3206
- 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>`;
3207
- createButton(true, currentPage > 1, getIcon("first"), () => {
3208
- currentPage = 1;
3209
- loadData();
3210
- });
3211
- createButton(true, currentPage > 1, getIcon("prev"), () => {
3212
- currentPage--;
3213
- loadData();
3214
- });
3215
- const maxVisible = 5;
3216
- let startPage = Math.max(1, currentPage - Math.floor(maxVisible / 2));
3217
- let endPage = Math.min(totalPages, startPage + maxVisible - 1);
3218
- if (endPage - startPage < maxVisible - 1) {
3219
- startPage = Math.max(1, endPage - maxVisible + 1);
3220
- }
3221
- for (let i = startPage; i <= endPage; i++) {
3222
- createButton(false, i !== currentPage, String(i), () => {
3223
- currentPage = i;
3224
- loadData();
3225
- });
3226
- }
3227
- createButton(true, currentPage < totalPages, getIcon("next"), () => {
3228
- currentPage++;
3229
- loadData();
3230
- });
3231
- createButton(true, currentPage < totalPages, getIcon("last"), () => {
3232
- currentPage = totalPages;
3233
- loadData();
3234
- });
3235
- }
3236
- function updateSortIndicators() {
3237
- sortButtons.forEach((btn) => {
3238
- const headerCell = btn.closest("th");
3239
- if (!headerCell) return;
3240
- const field = headerCell.getAttribute("data-sort");
3241
- const order = btn.getAttribute("data-order");
3242
- const isActive = sortField === field && sortOrder === order;
3243
- btn.classList.toggle(`${PREFIX}-table__sort-button--active`, isActive);
3298
+ <div class="${PREFIX}-pagination__page-size-container">
3299
+ <span class="${PREFIX}-pagination__page-size-label">Baris per halaman</span>
3300
+ <select class="${PREFIX}-pagination__page-size-select">
3301
+ ${this.options.pageSizeOptions.map(
3302
+ (opt) => `<option value="${opt}" ${this.state.pageSize === opt ? "selected" : ""}>${opt}</option>`
3303
+ ).join("")}
3304
+ </select>
3305
+ </div>
3306
+ </div>
3307
+ `;
3308
+ const navButtons = this.elements.pagination.querySelector(
3309
+ `.${PREFIX}-pagination__nav-buttons`
3310
+ );
3311
+ const pageSizeSelect = this.elements.pagination.querySelector(
3312
+ `.${PREFIX}-pagination__page-size-select`
3313
+ );
3314
+ pageSizeSelect.addEventListener("change", (e) => {
3315
+ this.state.pageSize = parseInt(e.target.value, 10);
3316
+ this.state.currentPage = 1;
3317
+ this.loadData();
3318
+ });
3319
+ const createBtn = (isNav, enabled, content, onClick) => {
3320
+ const btn = document.createElement("button");
3321
+ btn.type = "button";
3322
+ const baseCls = isNav ? `${PREFIX}-pagination__nav-button` : `${PREFIX}-pagination__page-button`;
3323
+ const stCls = isNav ? enabled ? `${baseCls}--enabled` : `${baseCls}--disabled` : enabled ? `${baseCls}--enabled` : `${baseCls}--active`;
3324
+ btn.className = `${baseCls} ${stCls}`;
3325
+ btn.disabled = !enabled;
3326
+ btn.innerHTML = content;
3327
+ btn.onclick = onClick;
3328
+ navButtons.appendChild(btn);
3329
+ };
3330
+ const SVG = {
3331
+ 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>',
3332
+ 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>',
3333
+ 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>',
3334
+ 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>'
3335
+ };
3336
+ createBtn(true, this.state.currentPage > 1, SVG.first, () => {
3337
+ this.state.currentPage = 1;
3338
+ this.loadData();
3339
+ });
3340
+ createBtn(true, this.state.currentPage > 1, SVG.prev, () => {
3341
+ this.state.currentPage--;
3342
+ this.loadData();
3343
+ });
3344
+ const maxVisible = 5;
3345
+ let startPage = Math.max(
3346
+ 1,
3347
+ this.state.currentPage - Math.floor(maxVisible / 2)
3348
+ );
3349
+ let endPage = Math.min(this.state.totalPages, startPage + maxVisible - 1);
3350
+ if (endPage - startPage < maxVisible - 1) {
3351
+ startPage = Math.max(1, endPage - maxVisible + 1);
3352
+ }
3353
+ for (let i = startPage; i <= endPage; i++) {
3354
+ createBtn(false, i !== this.state.currentPage, String(i), () => {
3355
+ this.state.currentPage = i;
3356
+ this.loadData();
3244
3357
  });
3245
3358
  }
3246
- async function loadData() {
3247
- setLoading(true);
3248
- try {
3249
- if (typeof fetchData === "function") {
3250
- const result = await fetchData({
3251
- page: currentPage,
3252
- pageSize,
3253
- sortField,
3254
- sortOrder,
3255
- searchTerm
3359
+ createBtn(
3360
+ true,
3361
+ this.state.currentPage < this.state.totalPages,
3362
+ SVG.next,
3363
+ () => {
3364
+ this.state.currentPage++;
3365
+ this.loadData();
3366
+ }
3367
+ );
3368
+ createBtn(
3369
+ true,
3370
+ this.state.currentPage < this.state.totalPages,
3371
+ SVG.last,
3372
+ () => {
3373
+ this.state.currentPage = this.state.totalPages;
3374
+ this.loadData();
3375
+ }
3376
+ );
3377
+ }
3378
+ updateSortIndicators() {
3379
+ const btns = this.elements.theadTr.querySelectorAll(
3380
+ `.${PREFIX}-table__sort-button`
3381
+ );
3382
+ btns.forEach((btn) => {
3383
+ const th = btn.closest("th");
3384
+ if (!th) return;
3385
+ const field = th.getAttribute("data-sort");
3386
+ const order = btn.getAttribute("data-order");
3387
+ const active = this.state.sortField === field && this.state.sortOrder === order;
3388
+ btn.classList.toggle(`${PREFIX}-table__sort-button--active`, active);
3389
+ });
3390
+ }
3391
+ async loadData() {
3392
+ this.setLoading(true);
3393
+ try {
3394
+ if (typeof this.options.fetchData === "function") {
3395
+ const res = await this.options.fetchData({
3396
+ page: this.state.currentPage,
3397
+ pageSize: this.state.pageSize,
3398
+ sortField: this.state.sortField,
3399
+ sortOrder: this.state.sortOrder,
3400
+ searchTerm: this.state.searchTerm
3401
+ });
3402
+ this.state.total = res.total;
3403
+ this.state.currentData = res.data || [];
3404
+ } else {
3405
+ let filtered = [...this.options.data];
3406
+ if (this.state.searchTerm) {
3407
+ const lower = this.state.searchTerm.toLowerCase();
3408
+ filtered = filtered.filter(
3409
+ (row) => this.options.columns.some(
3410
+ (c) => String(row[c.accessor] || "").toLowerCase().includes(lower)
3411
+ )
3412
+ );
3413
+ }
3414
+ if (this.state.sortField && this.state.sortOrder) {
3415
+ filtered.sort((a, b) => {
3416
+ const aVal = a[this.state.sortField];
3417
+ const bVal = b[this.state.sortField];
3418
+ if (aVal < bVal) return this.state.sortOrder === "asc" ? -1 : 1;
3419
+ if (aVal > bVal) return this.state.sortOrder === "asc" ? 1 : -1;
3420
+ return 0;
3256
3421
  });
3257
- total = result.total;
3258
- currentData = result.data;
3259
- } else {
3260
- let filteredData = [...data];
3261
- if (searchTerm) {
3262
- const lowerSearch = searchTerm.toLowerCase();
3263
- filteredData = filteredData.filter(
3264
- (item) => columns.some(
3265
- (c) => String(item[c.accessor] || "").toLowerCase().includes(lowerSearch)
3266
- )
3267
- );
3268
- }
3269
- if (sortField && sortOrder) {
3270
- filteredData.sort((a, b) => {
3271
- const aVal = a[sortField];
3272
- const bVal = b[sortField];
3273
- if (aVal < bVal) return sortOrder === "asc" ? -1 : 1;
3274
- if (aVal > bVal) return sortOrder === "asc" ? 1 : -1;
3275
- return 0;
3276
- });
3277
- }
3278
- total = filteredData.length;
3279
- const skip = (currentPage - 1) * pageSize;
3280
- currentData = filteredData.slice(skip, skip + pageSize);
3281
3422
  }
3282
- renderRows(currentData);
3283
- renderPagination();
3284
- updateSortIndicators();
3285
- } catch (error) {
3286
- console.error("Table API Error:", error);
3287
- } finally {
3288
- setLoading(false);
3423
+ this.state.total = filtered.length;
3424
+ const skip = (this.state.currentPage - 1) * this.state.pageSize;
3425
+ this.state.currentData = filtered.slice(
3426
+ skip,
3427
+ skip + this.state.pageSize
3428
+ );
3289
3429
  }
3430
+ this.renderRows(this.state.currentData);
3431
+ this.renderPagination();
3432
+ this.updateSortIndicators();
3433
+ } catch (e) {
3434
+ console.error("Table API Error:", e);
3435
+ } finally {
3436
+ this.setLoading(false);
3290
3437
  }
3291
- if (searchInputEl && searchBtnEl) {
3292
- const handleSearch = () => {
3293
- searchTerm = searchInputEl.value.trim();
3294
- currentPage = 1;
3295
- loadData();
3296
- if (typeof onSearch === "function") onSearch(searchTerm);
3297
- };
3298
- searchBtnEl.addEventListener("click", handleSearch);
3299
- searchInputEl.addEventListener("keydown", (e) => {
3300
- if (e.key === "Enter") handleSearch();
3301
- });
3438
+ }
3439
+ toggleRowSelection(keyStr, row, checked) {
3440
+ if (checked) {
3441
+ this.state.selectedKeys.add(keyStr);
3442
+ this.state.selectedRows.set(keyStr, row);
3443
+ } else {
3444
+ this.state.selectedKeys.delete(keyStr);
3445
+ this.state.selectedRows.delete(keyStr);
3302
3446
  }
3303
- loadData();
3304
- container.__tableAPI = {
3305
- reload: loadData,
3306
- setSearchTerm: (term) => {
3307
- searchTerm = term;
3308
- currentPage = 1;
3309
- loadData();
3310
- },
3311
- setPage: (page) => {
3312
- currentPage = page;
3313
- loadData();
3447
+ const inputs = this.elements.tbody.querySelectorAll(
3448
+ `.${PREFIX}-checkbox__input`
3449
+ );
3450
+ inputs.forEach((input) => {
3451
+ const box = input.nextElementSibling;
3452
+ if (input.checked) {
3453
+ box.classList.remove(`${PREFIX}-checkbox__box--unchecked`);
3454
+ box.classList.add(`${PREFIX}-checkbox__box--checked`);
3455
+ 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>';
3456
+ } else {
3457
+ box.classList.remove(`${PREFIX}-checkbox__box--checked`);
3458
+ box.classList.add(`${PREFIX}-checkbox__box--unchecked`);
3459
+ box.innerHTML = "";
3460
+ }
3461
+ });
3462
+ this.updateSelectAllState();
3463
+ this.triggerSelectionChange();
3464
+ }
3465
+ toggleAllSelection(checked) {
3466
+ this.state.currentData.forEach((row, index) => {
3467
+ const keyStr = String(row[this.options.rowKey] || index);
3468
+ if (checked) {
3469
+ this.state.selectedKeys.add(keyStr);
3470
+ this.state.selectedRows.set(keyStr, row);
3471
+ } else {
3472
+ this.state.selectedKeys.delete(keyStr);
3473
+ this.state.selectedRows.delete(keyStr);
3314
3474
  }
3475
+ });
3476
+ this.renderRows(this.state.currentData);
3477
+ this.triggerSelectionChange();
3478
+ }
3479
+ updateSelectAllState() {
3480
+ if (!this.options.selectable || !this.elements.selectAllInput) return;
3481
+ const checkboxes = this.elements.tbody.querySelectorAll(
3482
+ 'input[type="checkbox"]'
3483
+ );
3484
+ const checkedCount = Array.from(checkboxes).filter(
3485
+ (cb) => cb.checked
3486
+ ).length;
3487
+ const totalCount = this.state.currentData.length;
3488
+ const box = this.elements.selectAllBox;
3489
+ box.innerHTML = "";
3490
+ if (totalCount === 0 || checkedCount === 0) {
3491
+ this.elements.selectAllInput.checked = false;
3492
+ this.elements.selectAllInput.indeterminate = false;
3493
+ box.classList.remove(
3494
+ `${PREFIX}-checkbox__box--checked`,
3495
+ `${PREFIX}-checkbox__box--indeterminate`
3496
+ );
3497
+ box.classList.add(`${PREFIX}-checkbox__box--unchecked`);
3498
+ } else if (checkedCount === totalCount) {
3499
+ this.elements.selectAllInput.checked = true;
3500
+ this.elements.selectAllInput.indeterminate = false;
3501
+ box.classList.remove(
3502
+ `${PREFIX}-checkbox__box--unchecked`,
3503
+ `${PREFIX}-checkbox__box--indeterminate`
3504
+ );
3505
+ box.classList.add(`${PREFIX}-checkbox__box--checked`);
3506
+ 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>';
3507
+ } else {
3508
+ this.elements.selectAllInput.checked = false;
3509
+ this.elements.selectAllInput.indeterminate = true;
3510
+ box.classList.remove(
3511
+ `${PREFIX}-checkbox__box--unchecked`,
3512
+ `${PREFIX}-checkbox__box--checked`
3513
+ );
3514
+ box.classList.add(`${PREFIX}-checkbox__box--indeterminate`);
3515
+ 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>';
3516
+ }
3517
+ }
3518
+ triggerSelectionChange() {
3519
+ if (typeof this.options.onSelectionChange === "function") {
3520
+ const keys = Array.from(this.state.selectedKeys);
3521
+ const rows = Array.from(this.state.selectedRows.values());
3522
+ this.options.onSelectionChange(keys, rows);
3523
+ }
3524
+ }
3525
+ // API Methods
3526
+ reload() {
3527
+ this.loadData();
3528
+ }
3529
+ setSearchTerm(term) {
3530
+ this.state.searchTerm = term;
3531
+ this.state.currentPage = 1;
3532
+ if (this.elements.searchInput) {
3533
+ this.elements.searchInput.value = term;
3534
+ }
3535
+ this.loadData();
3536
+ }
3537
+ setPage(page) {
3538
+ this.state.currentPage = page;
3539
+ this.loadData();
3540
+ }
3541
+ getSelectedRows() {
3542
+ return {
3543
+ keys: Array.from(this.state.selectedKeys),
3544
+ rows: Array.from(this.state.selectedRows.values())
3315
3545
  };
3546
+ }
3547
+ };
3548
+ function initTable(selectorOrElement, options = {}) {
3549
+ const elements = typeof selectorOrElement === "string" ? document.querySelectorAll(selectorOrElement) : selectorOrElement ? typeof selectorOrElement.length !== "undefined" ? selectorOrElement : [selectorOrElement] : document.querySelectorAll(`.${PREFIX}-table`);
3550
+ const instances = [];
3551
+ elements.forEach((container) => {
3552
+ const instance = new Table(container, options);
3553
+ container.__tableAPI = instance;
3554
+ instances.push(instance);
3316
3555
  });
3556
+ if (instances.length === 0) return null;
3557
+ return instances.length === 1 ? instances[0] : instances;
3317
3558
  }
3318
3559
 
3319
3560
  // src/js/components/stateless/toast.js
@@ -3461,6 +3702,7 @@ function initAll() {
3461
3702
  }
3462
3703
  export {
3463
3704
  PREFIX,
3705
+ Table,
3464
3706
  initAll,
3465
3707
  setBrandTheme,
3466
3708
  showToast