@idds/js 1.0.85 → 1.0.86

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