@opengis/table 0.0.1

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.
package/README.md ADDED
@@ -0,0 +1,166 @@
1
+ # DataTable Component
2
+
3
+ Повнофункціональний компонент таблиці для Vue 3 з TypeScript, який підтримує пагінацію, вибір рядків, сортування та кастомні слоти.
4
+
5
+ ## Особливості
6
+
7
+ - ✅ Пагінація з локальними даними або через API
8
+ - ✅ Вибір рядків з чекбоксами
9
+ - ✅ Різні розміри таблиці (small, medium, large)
10
+ - ✅ Форматування даних (текст, числа, дати, бейджі, масиви)
11
+ - ✅ **Кастомні слоти для колонок**
12
+ - ✅ Стан завантаження та обробка помилок
13
+ - ✅ Темна тема
14
+ - ✅ Адаптивний дизайн
15
+
16
+ ## Кастомні слоти колонок
17
+
18
+ Компонент підтримує кастомні слоти для створення власної верстки в колонках. Використовуйте `template #body` в компоненті `Column`:
19
+
20
+ ```vue
21
+ <DataTable :rows="data" tableClass="w-full" tableStyle="min-width: 50rem">
22
+ <Column field="name" header="Назва">
23
+ <template #body="{ data }">
24
+ <div>
25
+ <div class="font-medium">{{ data.name.main }}</div>
26
+ <div class="text-sm text-gray-500">{{ data.name.sub }}</div>
27
+ </div>
28
+ </template>
29
+ </Column>
30
+ <Column field="status" header="Статус">
31
+ <template #body="{ data }">
32
+ <span :class="getStatusClass(data.status)" class="px-2 py-1 rounded-full text-xs">
33
+ {{ data.status }}
34
+ </span>
35
+ </template>
36
+ </Column>
37
+ <Column field="actions" header="Дії">
38
+ <template #body="{ data }">
39
+ <div class="flex gap-2">
40
+ <button @click="handleView(data)" title="Переглянути">
41
+ <i class="fas fa-eye"></i>
42
+ </button>
43
+ <button @click="handleEdit(data)" title="Редагувати">
44
+ <i class="fas fa-edit"></i>
45
+ </button>
46
+ </div>
47
+ </template>
48
+ </Column>
49
+ </DataTable>
50
+ ```
51
+
52
+ ### Параметри слоту
53
+
54
+ Слот `#body` отримує об'єкт з наступними властивостями:
55
+
56
+ - `data` - повний об'єкт рядка даних
57
+ - `value` - значення конкретного поля колонки
58
+
59
+ ### Приклади використання
60
+
61
+ #### 1. Складена назва з підзаголовком
62
+ ```vue
63
+ <Column field="name" header="Назва">
64
+ <template #body="{ data }">
65
+ <div>
66
+ <div class="font-medium">{{ data.name.main }}</div>
67
+ <div class="text-sm text-gray-500">{{ data.name.sub }}</div>
68
+ </div>
69
+ </template>
70
+ </Column>
71
+ ```
72
+
73
+ #### 2. Бейдж з кольором
74
+ ```vue
75
+ <Column field="role" header="Роль">
76
+ <template #body="{ data }">
77
+ <span :class="data.role === 'Заявник' ? 'bg-blue-100 text-blue-800' : 'bg-green-100 text-green-800'"
78
+ class="px-2 py-1 rounded-full text-xs font-medium">
79
+ {{ data.role }}
80
+ </span>
81
+ </template>
82
+ </Column>
83
+ ```
84
+
85
+ #### 3. Статус з іконкою
86
+ ```vue
87
+ <Column field="status" header="Статус">
88
+ <template #body="{ data }">
89
+ <div class="flex items-center gap-2">
90
+ <span :class="getStatusIconClass(data.status)" class="text-lg">
91
+ {{ getStatusIcon(data.status) }}
92
+ </span>
93
+ <span>{{ data.status }}</span>
94
+ </div>
95
+ </template>
96
+ </Column>
97
+ ```
98
+
99
+ #### 4. Кнопки дій
100
+ ```vue
101
+ <Column field="actions" header="Дії">
102
+ <template #body="{ data }">
103
+ <div class="flex gap-2">
104
+ <button @click.stop="handleView(data)" title="Переглянути">
105
+ <i class="fas fa-eye"></i>
106
+ </button>
107
+ <button @click.stop="handleDownload(data)" title="Завантажити">
108
+ <i class="fas fa-download"></i>
109
+ </button>
110
+ </div>
111
+ </template>
112
+ </Column>
113
+ ```
114
+
115
+ **Важливо:** Використовуйте `@click.stop` для кнопок в слотах, щоб уникнути спрацьовування кліку по рядку таблиці.
116
+
117
+ ## Встановлення
118
+
119
+ ```bash
120
+ npm install @your-org/datatable
121
+ ```
122
+
123
+ ## Використання
124
+
125
+ ```vue
126
+ <template>
127
+ <DataTable
128
+ :rows="data"
129
+ :columns="columns"
130
+ :selectable="true"
131
+ @update:selectedRows="handleSelection"
132
+ />
133
+ </template>
134
+
135
+ <script setup>
136
+ import { DataTable, Column } from '@your-org/datatable';
137
+
138
+ const data = ref([
139
+ { id: 1, name: 'Product A', category: 'Electronics' },
140
+ { id: 2, name: 'Product B', category: 'Books' }
141
+ ]);
142
+
143
+ const columns = ref([
144
+ { field: 'id', header: 'ID' },
145
+ { field: 'name', header: 'Name' },
146
+ { field: 'category', header: 'Category' }
147
+ ]);
148
+ </script>
149
+ ```
150
+
151
+ ## Пропси
152
+
153
+ | Пропс | Тип | За замовчуванням | Опис |
154
+ |-------|-----|------------------|------|
155
+ | `theme` | `'light' \| 'dark' \| 'auto'` | `'light'` | Тема таблиці |
156
+
157
+ ## Встановлення
158
+
159
+ ```bash
160
+ npm install
161
+ npm run dev
162
+ ```
163
+
164
+ ## Ліцензія
165
+
166
+ MIT
package/dist/index.css ADDED
@@ -0,0 +1 @@
1
+ .badge[data-v-9d7cd70c]{transition:all .2s ease-in-out}.badge[data-v-9d7cd70c]:hover{transform:scale(1.05)}
package/dist/index.mjs ADDED
@@ -0,0 +1,695 @@
1
+ import { defineComponent as S, computed as m, createElementBlock as n, openBlock as r, toDisplayString as x, normalizeStyle as re, normalizeClass as p, Fragment as A, renderList as z, createElementVNode as s, createTextVNode as O, ref as k, watch as j, onMounted as le, provide as me, useSlots as se, createCommentVNode as I, createBlock as K, resolveDynamicComponent as te, inject as fe, renderSlot as be, withDirectives as ye, vShow as he } from "vue";
2
+ const xe = /* @__PURE__ */ S({
3
+ name: "NumberFormat",
4
+ __name: "NumberFormat",
5
+ props: {
6
+ value: {}
7
+ },
8
+ setup(f) {
9
+ const l = f, e = m(() => l.value === null || l.value === void 0 ? "" : typeof l.value == "number" ? l.value.toLocaleString() : l.value);
10
+ return (i, t) => (r(), n("span", null, x(e.value), 1));
11
+ }
12
+ }), ke = /* @__PURE__ */ S({
13
+ name: "DateFormat",
14
+ __name: "DateFormat",
15
+ props: {
16
+ value: {}
17
+ },
18
+ setup(f) {
19
+ const l = f, e = m(() => {
20
+ if (!l.value) return "";
21
+ try {
22
+ return new Date(l.value).toLocaleDateString();
23
+ } catch {
24
+ return l.value;
25
+ }
26
+ });
27
+ return (i, t) => (r(), n("span", null, x(e.value), 1));
28
+ }
29
+ }), _e = /* @__PURE__ */ S({
30
+ name: "TextFormat",
31
+ __name: "TextFormat",
32
+ props: {
33
+ value: {}
34
+ },
35
+ setup(f) {
36
+ return (l, e) => (r(), n("span", null, x(l.value), 1));
37
+ }
38
+ }), we = /* @__PURE__ */ S({
39
+ name: "BadgeFormat",
40
+ __name: "BadgeFormat",
41
+ props: {
42
+ value: {}
43
+ },
44
+ setup(f) {
45
+ const l = f, e = m(() => {
46
+ const t = String(l.value || "").toLowerCase(), d = "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium";
47
+ return t.includes("success") || t.includes("active") || t.includes("completed") ? `${d} bg-green-100 text-green-800` : t.includes("error") || t.includes("failed") || t.includes("inactive") ? `${d} bg-red-100 text-red-800` : t.includes("warning") || t.includes("pending") ? `${d} bg-yellow-100 text-yellow-800` : t.includes("info") || t.includes("processing") ? `${d} bg-blue-100 text-blue-800` : `${d} bg-gray-100 text-gray-800`;
48
+ }), i = m(() => ({
49
+ minWidth: "60px",
50
+ textAlign: "center",
51
+ display: "inline-block"
52
+ }));
53
+ return (t, d) => (r(), n("span", {
54
+ class: p(["badge", e.value]),
55
+ style: re(i.value)
56
+ }, x(t.value), 7));
57
+ }
58
+ }), Ce = (f, l) => {
59
+ const e = f.__vccOpts || f;
60
+ for (const [i, t] of l)
61
+ e[i] = t;
62
+ return e;
63
+ }, $e = /* @__PURE__ */ Ce(we, [["__scopeId", "data-v-9d7cd70c"]]), Se = { class: "flex flex-wrap gap-1" }, Te = /* @__PURE__ */ S({
64
+ name: "ArrayFormat",
65
+ __name: "ArrayFormat",
66
+ props: {
67
+ value: {}
68
+ },
69
+ setup(f) {
70
+ const l = f, e = m(() => l.value ? Array.isArray(l.value) ? l.value : [l.value] : []), i = (t) => t ? typeof t == "object" && t.name ? t.name : typeof t == "string" || typeof t == "number" ? String(t) : JSON.stringify(t) : "";
71
+ return (t, d) => (r(), n("div", Se, [
72
+ (r(!0), n(A, null, z(e.value, (u, C) => (r(), n("span", {
73
+ key: C,
74
+ class: "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 hover:bg-blue-200 transition-colors"
75
+ }, x(i(u)), 1))), 128))
76
+ ]));
77
+ }
78
+ }), ae = {
79
+ number: xe,
80
+ date: ke,
81
+ text: _e,
82
+ badge: $e,
83
+ array: Te
84
+ }, Fe = { class: "flex items-center space-x-4" }, De = { class: "font-medium" }, Pe = { class: "font-medium" }, Ae = { class: "font-medium" }, Be = { class: "flex items-center space-x-2" }, Re = ["disabled"], ze = { class: "flex items-center space-x-1" }, Ie = ["onClick"], je = ["disabled"], ne = /* @__PURE__ */ S({
85
+ name: "Pagination",
86
+ __name: "Pagination",
87
+ props: {
88
+ page: {},
89
+ limit: {},
90
+ total: {},
91
+ theme: { default: "light" },
92
+ size: { default: "medium" }
93
+ },
94
+ emits: ["update:page"],
95
+ setup(f, { emit: l }) {
96
+ const e = f, i = m(() => ({
97
+ small: {
98
+ text: "text-xs",
99
+ button: "px-2 h-7 text-xs min-w-[28px] flex items-center justify-center",
100
+ icon: "w-3.5 h-3.5"
101
+ },
102
+ medium: {
103
+ text: "text-sm",
104
+ button: "px-3 h-9 text-sm min-w-[36px] flex items-center justify-center",
105
+ icon: "w-4 h-4"
106
+ },
107
+ large: {
108
+ text: "text-base",
109
+ button: "px-4 h-11 text-base min-w-[44px] flex items-center justify-center",
110
+ icon: "w-5 h-5"
111
+ }
112
+ })[e.size]), t = m(() => {
113
+ const { theme: c } = e;
114
+ return c === "dark" ? {
115
+ container: "bg-gray-900 border-gray-700",
116
+ text: "text-gray-300",
117
+ button: "text-gray-300 bg-gray-800 border-gray-600 hover:bg-gray-700",
118
+ buttonDisabled: "text-gray-500 bg-gray-800",
119
+ buttonActive: "bg-blue-600 text-white",
120
+ ellipsis: "text-gray-400"
121
+ } : c === "light" ? {
122
+ container: "bg-white border-gray-200",
123
+ text: "text-gray-700",
124
+ button: "text-gray-700 bg-white border-gray-300 hover:bg-gray-50",
125
+ buttonDisabled: "text-gray-400 bg-gray-100",
126
+ buttonActive: "bg-blue-600 text-white",
127
+ ellipsis: "text-gray-500"
128
+ } : {
129
+ container: "bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-700",
130
+ text: "text-gray-700 dark:text-gray-300",
131
+ button: "text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700",
132
+ buttonDisabled: "text-gray-400 dark:text-gray-500 bg-gray-100 dark:bg-gray-800",
133
+ buttonActive: "bg-blue-600 text-white",
134
+ ellipsis: "text-gray-500 dark:text-gray-400"
135
+ };
136
+ }), d = l, u = m(() => Math.ceil(e.total / e.limit)), C = m(() => e.total === 0 ? 0 : (e.page - 1) * e.limit + 1), T = m(() => Math.min(e.page * e.limit, e.total)), $ = m(() => {
137
+ const c = [];
138
+ if (u.value <= 7)
139
+ for (let h = 1; h <= u.value; h += 1)
140
+ c.push(h);
141
+ else {
142
+ c.push(1), e.page > 4 && c.push("...");
143
+ const h = Math.max(2, e.page - 1), B = Math.min(u.value - 1, e.page + 1);
144
+ for (let F = h; F <= B; F += 1)
145
+ F !== 1 && F !== u.value && c.push(F);
146
+ e.page < u.value - 3 && c.push("..."), u.value > 1 && c.push(u.value);
147
+ }
148
+ return c;
149
+ }), w = (c) => {
150
+ c >= 1 && c <= u.value && c !== e.page && d("update:page", c);
151
+ };
152
+ return (c, b) => (r(), n("div", {
153
+ class: p(["flex items-center justify-between py-3 border-t", t.value.container])
154
+ }, [
155
+ s("div", Fe, [
156
+ s("div", {
157
+ class: p(["flex items-center text-sm", t.value.text, i.value.text])
158
+ }, [
159
+ s("span", null, [
160
+ b[2] || (b[2] = O(" Показано ", -1)),
161
+ s("span", De, x(C.value), 1),
162
+ b[3] || (b[3] = O(" до ", -1)),
163
+ s("span", Pe, x(T.value), 1),
164
+ b[4] || (b[4] = O(" з ", -1)),
165
+ s("span", Ae, x(c.total), 1),
166
+ b[5] || (b[5] = O(" результатів ", -1))
167
+ ])
168
+ ], 2)
169
+ ]),
170
+ s("div", Be, [
171
+ s("button", {
172
+ onClick: b[0] || (b[0] = (h) => w(c.page - 1)),
173
+ disabled: c.page <= 1,
174
+ class: p([
175
+ "relative font-medium rounded-md",
176
+ i.value.button,
177
+ c.page <= 1 ? t.value.buttonDisabled : t.value.button
178
+ ])
179
+ }, [
180
+ (r(), n("svg", {
181
+ class: p(i.value.icon),
182
+ fill: "none",
183
+ stroke: "currentColor",
184
+ viewBox: "0 0 24 24"
185
+ }, b[6] || (b[6] = [
186
+ s("path", {
187
+ "stroke-linecap": "round",
188
+ "stroke-linejoin": "round",
189
+ "stroke-width": "2",
190
+ d: "M15 19l-7-7 7-7"
191
+ }, null, -1)
192
+ ]), 2))
193
+ ], 10, Re),
194
+ s("div", ze, [
195
+ (r(!0), n(A, null, z($.value, (h) => (r(), n(A, { key: h }, [
196
+ h === "..." ? (r(), n("span", {
197
+ key: 0,
198
+ class: p(["font-medium", i.value.button, t.value.ellipsis])
199
+ }, " ... ", 2)) : (r(), n("button", {
200
+ key: 1,
201
+ onClick: (B) => w(h),
202
+ class: p([
203
+ "relative font-medium rounded-md",
204
+ i.value.button,
205
+ h === c.page ? t.value.buttonActive : t.value.button
206
+ ])
207
+ }, x(h), 11, Ie))
208
+ ], 64))), 128))
209
+ ]),
210
+ s("button", {
211
+ onClick: b[1] || (b[1] = (h) => w(c.page + 1)),
212
+ disabled: c.page >= u.value,
213
+ class: p([
214
+ "relative font-medium rounded-md",
215
+ i.value.button,
216
+ c.page >= u.value ? t.value.buttonDisabled : t.value.button
217
+ ])
218
+ }, [
219
+ (r(), n("svg", {
220
+ class: p(i.value.icon),
221
+ fill: "none",
222
+ stroke: "currentColor",
223
+ viewBox: "0 0 24 24"
224
+ }, b[7] || (b[7] = [
225
+ s("path", {
226
+ "stroke-linecap": "round",
227
+ "stroke-linejoin": "round",
228
+ "stroke-width": "2",
229
+ d: "M9 5l7 7-7 7"
230
+ }, null, -1)
231
+ ]), 2))
232
+ ], 10, je)
233
+ ])
234
+ ], 2));
235
+ }
236
+ }), Me = {
237
+ key: 0,
238
+ class: "text-center py-8"
239
+ }, Ne = { class: "inline-flex items-center space-x-2" }, Ve = {
240
+ key: 2,
241
+ class: "overflow-x-auto"
242
+ }, Le = { class: "relative w-full overflow-auto" }, He = ["checked", "indeterminate"], Ee = ["onClick"], Ue = { class: "flex items-center justify-between" }, Oe = {
243
+ key: 0,
244
+ class: "ml-1 text-xs"
245
+ }, Je = ["onClick"], We = ["checked", "onChange"], oe = /* @__PURE__ */ S({
246
+ name: "DataTable",
247
+ __name: "DataTable",
248
+ props: {
249
+ rows: { default: () => [] },
250
+ columns: { default: () => [] },
251
+ api: {},
252
+ tableStyle: { default: "min-width: 50rem" },
253
+ tableClass: { default: "min-w-full divide-y divide-gray-200 dark:divide-gray-700" },
254
+ size: { default: "medium" },
255
+ theme: { default: "light" },
256
+ loading: { type: Boolean, default: !1 },
257
+ selectable: { type: Boolean, default: !1 },
258
+ selectedRows: { default: () => [] },
259
+ limit: { default: 10 },
260
+ page: { default: 1 },
261
+ total: { default: 0 },
262
+ showPagination: { type: Boolean, default: !0 },
263
+ sortable: { type: Boolean, default: !1 },
264
+ clickable: { type: Boolean, default: !1 }
265
+ },
266
+ emits: ["update:page", "update:selectedRows", "row-click"],
267
+ setup(f, { emit: l }) {
268
+ const e = f, i = l, t = k(e.selectedRows || []), d = k([]), u = k([]), C = k([]), T = k(!1), $ = k(null), w = m(() => {
269
+ if (e.api)
270
+ return u.value;
271
+ if (e.sortable && d.value.length > 0) {
272
+ const a = [...e.rows];
273
+ return a.sort((g, o) => {
274
+ for (const y of d.value) {
275
+ if (!y.direction)
276
+ return 0;
277
+ const v = g[y.field], P = o[y.field];
278
+ if (v == null) return 1;
279
+ if (P == null) return -1;
280
+ let D = 0;
281
+ typeof v == "string" && typeof P == "string" ? D = v.localeCompare(P) : v < P ? D = -1 : v > P ? D = 1 : D = 0;
282
+ const R = y.direction === "desc" ? -D : D;
283
+ if (R !== 0) return R;
284
+ }
285
+ return 0;
286
+ }), a;
287
+ }
288
+ return e.rows;
289
+ });
290
+ j(() => e.selectedRows, (a) => {
291
+ t.value = a || [];
292
+ });
293
+ const c = (a) => t.value.some((g) => g === a), b = (a) => {
294
+ const g = t.value.findIndex((o) => o === a);
295
+ g > -1 ? t.value.splice(g, 1) : t.value.push(a), i("update:selectedRows", [...t.value]);
296
+ }, h = m(() => w.value.length > 0 && t.value.length === w.value.length), B = m(() => t.value.length > 0 && t.value.length < w.value.length), F = () => {
297
+ h.value ? t.value = [] : t.value = [...w.value], i("update:selectedRows", [...t.value]);
298
+ }, ie = (a) => {
299
+ if (!e.sortable) return;
300
+ const g = d.value.findIndex((o) => o.field === a);
301
+ if (g >= 0) {
302
+ const o = d.value[g];
303
+ o.direction === "asc" ? o.direction = "desc" : o.direction === "desc" && d.value.splice(g, 1);
304
+ } else
305
+ d.value.unshift({
306
+ field: a,
307
+ direction: "asc"
308
+ });
309
+ e.api && V();
310
+ }, ue = (a) => {
311
+ const g = d.value.find((o) => o.field === a);
312
+ if (!g)
313
+ return "↑↓";
314
+ switch (g.direction) {
315
+ case "asc":
316
+ return "↑";
317
+ // Up arrow for ascending
318
+ case "desc":
319
+ return "↓";
320
+ // Down arrow for descending
321
+ default:
322
+ return "↕️";
323
+ }
324
+ }, J = (a) => e.sortable && a.sortable !== !1, ce = (a) => {
325
+ e.clickable && i("row-click", a);
326
+ }, W = k(e.page), Q = k(e.limit), q = k(e.total), M = k(1), G = k(10), N = k(0), X = m(() => e.api ? M.value : W.value), Y = m(() => e.api ? G.value : Q.value), Z = m(() => e.api ? N.value : q.value), _ = m(() => {
327
+ const { theme: a } = e;
328
+ return a === "dark" ? {
329
+ container: "dark",
330
+ loading: "text-gray-400",
331
+ error: "text-red-400",
332
+ thead: "bg-gray-800",
333
+ th: "text-gray-300",
334
+ thHover: "hover:bg-gray-700",
335
+ tbody: "bg-gray-900 divide-gray-700",
336
+ tr: "hover:bg-gray-800",
337
+ td: "text-gray-300",
338
+ empty: "text-gray-400",
339
+ checkbox: "border-gray-600 bg-gray-700 text-blue-400"
340
+ } : a === "light" ? {
341
+ container: "",
342
+ loading: "text-gray-500",
343
+ error: "text-red-500",
344
+ thead: "bg-gray-50",
345
+ th: "text-gray-500",
346
+ thHover: "hover:bg-gray-100",
347
+ tbody: "bg-white divide-gray-200",
348
+ tr: "hover:bg-gray-50",
349
+ td: "text-gray-900",
350
+ empty: "text-gray-500",
351
+ checkbox: "border-gray-300 bg-white text-blue-600"
352
+ } : {
353
+ container: "",
354
+ loading: "text-gray-500 dark:text-gray-400",
355
+ error: "text-red-500 dark:text-red-400",
356
+ thead: "bg-gray-50 dark:bg-gray-800",
357
+ th: "text-gray-500 dark:text-gray-300",
358
+ thHover: "hover:bg-gray-100 dark:hover:bg-gray-700",
359
+ tbody: "bg-white dark:bg-gray-900 divide-gray-200 dark:divide-gray-700",
360
+ tr: "hover:bg-gray-50 dark:hover:bg-gray-800",
361
+ td: "text-gray-900 dark:text-gray-300",
362
+ empty: "text-gray-500 dark:text-gray-400",
363
+ checkbox: "border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-blue-600 dark:text-blue-400"
364
+ };
365
+ });
366
+ j(() => e.page, (a) => {
367
+ W.value = a;
368
+ }), j(() => e.limit, (a) => {
369
+ Q.value = a;
370
+ }), j(() => e.total, (a) => {
371
+ q.value = a;
372
+ });
373
+ const V = async () => {
374
+ if (e.api) {
375
+ T.value = !0, $.value = null;
376
+ try {
377
+ const a = new URL(e.api, window.location.origin);
378
+ if (a.searchParams.set("page", X.value.toString()), a.searchParams.set("limit", Y.value.toString()), d.value.length > 0) {
379
+ const y = d.value.filter((v) => v.direction).map((v) => `${v.field}:${v.direction}`).join(",");
380
+ y && a.searchParams.set("sort", y);
381
+ }
382
+ const g = await fetch(a.toString());
383
+ if (!g.ok)
384
+ throw new Error(`HTTP error! status: ${g.status}`);
385
+ const o = await g.json();
386
+ o.rows && Array.isArray(o.rows) && (u.value = o.rows), o.columns && Array.isArray(o.columns) && (C.value = o.columns), o.total !== void 0 && (e.api ? N.value = o.total : q.value = o.total);
387
+ } catch (a) {
388
+ $.value = a instanceof Error ? a.message : "Failed to fetch data", console.error("Error fetching data:", a);
389
+ } finally {
390
+ T.value = !1;
391
+ }
392
+ }
393
+ }, de = (a) => {
394
+ e.api ? M.value = a : (W.value = a, i("update:page", a)), e.api && V();
395
+ }, ge = m(() => e.showPagination && Z.value > 0), L = m(() => ({
396
+ small: {
397
+ header: "px-3 py-2 text-xs",
398
+ cell: "px-3 py-2 text-xs"
399
+ },
400
+ medium: {
401
+ header: "px-6 py-3 text-xs",
402
+ cell: "px-6 py-4 text-sm"
403
+ },
404
+ large: {
405
+ header: "px-8 py-4 text-sm",
406
+ cell: "px-8 py-6 text-base"
407
+ }
408
+ })[e.size]);
409
+ le(() => {
410
+ e.api && (e.page && (M.value = e.page), e.limit && (G.value = e.limit), e.total && (N.value = e.total), V());
411
+ }), j(() => e.api, (a) => {
412
+ a && (M.value = e.page || 1, G.value = e.limit || 10, N.value = e.total || 0, V());
413
+ });
414
+ const H = m(() => e.api ? C.value : e.columns), E = k([]), U = k({});
415
+ me("registerColumn", (a, g) => {
416
+ E.value.push(a), g && a.slotName && (U.value[a.name] = g);
417
+ });
418
+ const ve = se(), ee = m(() => {
419
+ if (E.value.length > 0)
420
+ return console.log("Using slot columns:", E.value), E.value;
421
+ const a = ve.default?.();
422
+ if (a) {
423
+ console.log("Slot children:", a);
424
+ const g = a.filter((o) => {
425
+ const y = o.type?.name === "Column" || o.type === "Column" || o.type && typeof o.type == "object" && o.type.name === "Column";
426
+ return console.log("Checking child:", o.type, "isColumn:", y), y;
427
+ }).map((o) => {
428
+ console.log("Column slot props:", o.props), console.log("Column slot type:", o.type);
429
+ const y = o.props || o.componentProps || {}, v = y.field || y.name || "", P = y.header || y.label || "", D = o.children && o.children.body, R = {
430
+ name: v,
431
+ // Use field as the name for data access
432
+ ua: P,
433
+ // Use header as the display name
434
+ format: y.format || "text",
435
+ slotName: D ? "body" : void 0,
436
+ ...y
437
+ };
438
+ return D && R.name && (U.value[R.name] = o.children.body), console.log("Processed column:", R), R;
439
+ });
440
+ if (g.length > 0)
441
+ return console.log("Final processed columns from slots:", g), g;
442
+ }
443
+ return H.value && H.value.length > 0 ? (console.log("Using table columns from props/API:", H.value), H.value) : [];
444
+ }), pe = (a = "text") => ae[a] || ae.text;
445
+ return (a, g) => (r(), n("div", {
446
+ class: p(["w-full", _.value.container])
447
+ }, [
448
+ T.value || e.loading ? (r(), n("div", Me, [
449
+ s("div", Ne, [
450
+ g[0] || (g[0] = s("div", { class: "animate-spin rounded-full h-6 w-6 border-b-2 border-blue-600" }, null, -1)),
451
+ s("span", {
452
+ class: p(_.value.loading)
453
+ }, "Loading...", 2)
454
+ ])
455
+ ])) : $.value ? (r(), n("div", {
456
+ key: 1,
457
+ class: p(["text-center py-8", _.value.error])
458
+ }, x($.value), 3)) : !e.loading && w.value.length > 0 ? (r(), n("div", Ve, [
459
+ s("div", Le, [
460
+ s("table", {
461
+ class: p(a.tableClass),
462
+ style: re(a.tableStyle)
463
+ }, [
464
+ s("thead", {
465
+ class: p(_.value.thead)
466
+ }, [
467
+ s("tr", null, [
468
+ a.selectable ? (r(), n("th", {
469
+ key: 0,
470
+ class: p(["text-left font-medium uppercase tracking-wider", _.value.th, L.value.header, "w-12"])
471
+ }, [
472
+ s("input", {
473
+ type: "checkbox",
474
+ checked: h.value,
475
+ indeterminate: B.value,
476
+ onChange: F,
477
+ class: p(["h-4 w-4 focus:ring-blue-500 rounded", _.value.checkbox])
478
+ }, null, 42, He)
479
+ ], 2)) : I("", !0),
480
+ (r(!0), n(A, null, z(ee.value, (o) => (r(), n("th", {
481
+ key: o.name,
482
+ class: p([
483
+ "text-left font-medium uppercase tracking-wider",
484
+ _.value.th,
485
+ L.value.header,
486
+ o.align ? `text-${o.align}` : "",
487
+ J(o) ? ["cursor-pointer", _.value.thHover] : ""
488
+ ]),
489
+ onClick: (y) => J(o) ? ie(o.name) : void 0
490
+ }, [
491
+ s("div", Ue, [
492
+ s("span", null, x(o.ua || o.header || o.name), 1),
493
+ J(o) ? (r(), n("span", Oe, x(ue(o.name)), 1)) : I("", !0)
494
+ ])
495
+ ], 10, Ee))), 128))
496
+ ])
497
+ ], 2),
498
+ s("tbody", {
499
+ class: p(_.value.tbody)
500
+ }, [
501
+ (r(!0), n(A, null, z(w.value, (o, y) => (r(), n("tr", {
502
+ key: y,
503
+ class: p([
504
+ _.value.tr,
505
+ e.clickable ? "cursor-pointer" : ""
506
+ ]),
507
+ onClick: (v) => e.clickable ? ce(o) : void 0
508
+ }, [
509
+ a.selectable ? (r(), n("td", {
510
+ key: 0,
511
+ class: p(["whitespace-nowrap", _.value.td, L.value.cell, "w-12"])
512
+ }, [
513
+ s("input", {
514
+ type: "checkbox",
515
+ checked: c(o),
516
+ onChange: (v) => b(o),
517
+ class: p(["h-4 w-4 focus:ring-blue-500 rounded", _.value.checkbox])
518
+ }, null, 42, We)
519
+ ], 2)) : I("", !0),
520
+ (r(!0), n(A, null, z(ee.value, (v) => (r(), n("td", {
521
+ key: v.name,
522
+ class: p([
523
+ "whitespace-nowrap",
524
+ _.value.td,
525
+ L.value.cell,
526
+ v.align ? `text-${v.align}` : ""
527
+ ])
528
+ }, [
529
+ v.slotName && U.value[v.name] ? (r(), K(te(U.value[v.name]), {
530
+ key: 0,
531
+ data: o,
532
+ value: o[v.name]
533
+ }, null, 8, ["data", "value"])) : (r(), K(te(pe(v.format)), {
534
+ key: 1,
535
+ value: o[v.name]
536
+ }, null, 8, ["value"]))
537
+ ], 2))), 128))
538
+ ], 10, Je))), 128))
539
+ ], 2)
540
+ ], 6)
541
+ ])
542
+ ])) : I("", !0),
543
+ ge.value && !T.value && !e.loading && !$.value ? (r(), K(ne, {
544
+ key: 3,
545
+ page: X.value,
546
+ limit: Y.value,
547
+ total: Z.value,
548
+ theme: e.theme,
549
+ size: e.size,
550
+ "onUpdate:page": de
551
+ }, null, 8, ["page", "limit", "total", "theme", "size"])) : I("", !0),
552
+ !T.value && !e.loading && !$.value && w.value.length === 0 ? (r(), n("div", {
553
+ key: 4,
554
+ class: p(["text-center py-8", _.value.empty])
555
+ }, " Дані відсутні ", 2)) : I("", !0)
556
+ ], 2));
557
+ }
558
+ }), qe = { style: { display: "none" } }, Ge = /* @__PURE__ */ S({
559
+ name: "Column",
560
+ __name: "Column",
561
+ props: {
562
+ field: {},
563
+ name: {},
564
+ header: {},
565
+ label: {},
566
+ format: {},
567
+ hidden: { type: Boolean },
568
+ sortable: { type: Boolean },
569
+ width: {},
570
+ align: {},
571
+ slot: {},
572
+ slotName: {}
573
+ },
574
+ setup(f) {
575
+ const l = f, e = se(), i = fe("registerColumn");
576
+ return le(() => {
577
+ if (i) {
578
+ const {
579
+ field: t,
580
+ name: d,
581
+ header: u,
582
+ label: C,
583
+ format: T,
584
+ slotName: $,
585
+ ...w
586
+ } = l, B = {
587
+ name: t || d || "",
588
+ ua: u || C || "",
589
+ format: T || "text",
590
+ slotName: $ || (e.body ? "body" : void 0),
591
+ // Use 'body' slot if it exists
592
+ ...w
593
+ };
594
+ console.log("Registering column:", B);
595
+ const F = e.body;
596
+ i(B, F);
597
+ }
598
+ }), (t, d) => (r(), n("div", qe, [
599
+ be(t.$slots, "body", { data: {} })
600
+ ]));
601
+ }
602
+ }), Ke = { class: "mt-4" }, Qe = { class: "text-sm text-gray-600 dark:text-gray-300" }, Xe = /* @__PURE__ */ S({
603
+ __name: "SelectionInfo",
604
+ props: {
605
+ selectedRows: {},
606
+ totalCount: {}
607
+ },
608
+ setup(f) {
609
+ const l = f, e = m(() => l.selectedRows.length);
610
+ return (i, t) => (r(), n("div", Ke, [
611
+ s("div", Qe, " Обрано: " + x(e.value) + " з " + x(i.totalCount) + " записів ", 1)
612
+ ]));
613
+ }
614
+ }), Ye = { class: "mt-4" }, Ze = { class: "border-b border-gray-200 dark:border-gray-700" }, et = { class: "-mb-px flex space-x-8" }, tt = ["onClick"], at = { class: "mt-4" }, ot = { class: "bg-gray-900 rounded-lg overflow-hidden" }, rt = { class: "flex items-center justify-between px-4 py-2 bg-gray-800 border-b border-gray-700" }, lt = { class: "text-sm text-gray-300" }, st = ["onClick"], nt = { class: "p-4 text-sm text-gray-100 overflow-x-auto" }, it = /* @__PURE__ */ S({
615
+ __name: "CodeTabs",
616
+ props: {
617
+ tabs: {},
618
+ defaultTab: { default: void 0 }
619
+ },
620
+ setup(f) {
621
+ const l = f, e = k(l.defaultTab || l.tabs[0]?.id || ""), i = async (t) => {
622
+ try {
623
+ await navigator.clipboard.writeText(t), console.log("Код скопійовано!");
624
+ } catch (d) {
625
+ console.error("Помилка копіювання:", d);
626
+ }
627
+ };
628
+ return (t, d) => (r(), n("div", Ye, [
629
+ s("div", Ze, [
630
+ s("nav", et, [
631
+ (r(!0), n(A, null, z(t.tabs, (u) => (r(), n("button", {
632
+ key: u.id,
633
+ onClick: (C) => e.value = u.id,
634
+ class: p([
635
+ "py-2 px-1 border-b-2 font-medium text-sm transition-colors",
636
+ e.value === u.id ? "border-blue-500 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300"
637
+ ])
638
+ }, x(u.label), 11, tt))), 128))
639
+ ])
640
+ ]),
641
+ s("div", at, [
642
+ (r(!0), n(A, null, z(t.tabs, (u) => ye((r(), n("div", {
643
+ key: u.id
644
+ }, [
645
+ s("div", ot, [
646
+ s("div", rt, [
647
+ s("span", lt, x(u.label), 1),
648
+ s("button", {
649
+ onClick: (C) => i(u.content),
650
+ class: "text-gray-400 hover:text-white transition-colors",
651
+ title: "Копіювати код"
652
+ }, d[0] || (d[0] = [
653
+ s("svg", {
654
+ class: "w-4 h-4",
655
+ fill: "none",
656
+ stroke: "currentColor",
657
+ viewBox: "0 0 24 24"
658
+ }, [
659
+ s("path", {
660
+ "stroke-linecap": "round",
661
+ "stroke-linejoin": "round",
662
+ "stroke-width": "2",
663
+ d: "M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
664
+ })
665
+ ], -1)
666
+ ]), 8, st)
667
+ ]),
668
+ s("pre", nt, [
669
+ s("code", null, x(u.content), 1)
670
+ ])
671
+ ])
672
+ ])), [
673
+ [he, e.value === u.id]
674
+ ])), 128))
675
+ ])
676
+ ]));
677
+ }
678
+ });
679
+ oe.install = function(l) {
680
+ l.component("DataTable", oe), l.component("Column", Ge), l.component("Pagination", ne), l.component("SelectionInfo", Xe), l.component("CodeTabs", it);
681
+ };
682
+ export {
683
+ Te as ArrayFormat,
684
+ $e as BadgeFormat,
685
+ it as CodeTabs,
686
+ Ge as Column,
687
+ oe as DataTable,
688
+ ke as DateFormat,
689
+ xe as NumberFormat,
690
+ ne as Pagination,
691
+ Xe as SelectionInfo,
692
+ _e as TextFormat,
693
+ oe as default,
694
+ ae as formatComponents
695
+ };
@@ -0,0 +1 @@
1
+ (function(f,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(f=typeof globalThis<"u"?globalThis:f||self,e(f.filter={},f.Vue))})(this,function(f,e){"use strict";const M=e.defineComponent({name:"NumberFormat",__name:"NumberFormat",props:{value:{}},setup(p){const n=p,t=e.computed(()=>n.value===null||n.value===void 0?"":typeof n.value=="number"?n.value.toLocaleString():n.value);return(r,a)=>(e.openBlock(),e.createElementBlock("span",null,e.toDisplayString(t.value),1))}}),H=e.defineComponent({name:"DateFormat",__name:"DateFormat",props:{value:{}},setup(p){const n=p,t=e.computed(()=>{if(!n.value)return"";try{return new Date(n.value).toLocaleDateString()}catch{return n.value}});return(r,a)=>(e.openBlock(),e.createElementBlock("span",null,e.toDisplayString(t.value),1))}}),U=e.defineComponent({name:"TextFormat",__name:"TextFormat",props:{value:{}},setup(p){return(n,t)=>(e.openBlock(),e.createElementBlock("span",null,e.toDisplayString(n.value),1))}}),O=((p,n)=>{const t=p.__vccOpts||p;for(const[r,a]of n)t[r]=a;return t})(e.defineComponent({name:"BadgeFormat",__name:"BadgeFormat",props:{value:{}},setup(p){const n=p,t=e.computed(()=>{const a=String(n.value||"").toLowerCase(),c="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium";return a.includes("success")||a.includes("active")||a.includes("completed")?`${c} bg-green-100 text-green-800`:a.includes("error")||a.includes("failed")||a.includes("inactive")?`${c} bg-red-100 text-red-800`:a.includes("warning")||a.includes("pending")?`${c} bg-yellow-100 text-yellow-800`:a.includes("info")||a.includes("processing")?`${c} bg-blue-100 text-blue-800`:`${c} bg-gray-100 text-gray-800`}),r=e.computed(()=>({minWidth:"60px",textAlign:"center",display:"inline-block"}));return(a,c)=>(e.openBlock(),e.createElementBlock("span",{class:e.normalizeClass(["badge",t.value]),style:e.normalizeStyle(r.value)},e.toDisplayString(a.value),7))}}),[["__scopeId","data-v-9d7cd70c"]]),ee={class:"flex flex-wrap gap-1"},q=e.defineComponent({name:"ArrayFormat",__name:"ArrayFormat",props:{value:{}},setup(p){const n=p,t=e.computed(()=>n.value?Array.isArray(n.value)?n.value:[n.value]:[]),r=a=>a?typeof a=="object"&&a.name?a.name:typeof a=="string"||typeof a=="number"?String(a):JSON.stringify(a):"";return(a,c)=>(e.openBlock(),e.createElementBlock("div",ee,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.value,(s,k)=>(e.openBlock(),e.createElementBlock("span",{key:k,class:"inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 hover:bg-blue-200 transition-colors"},e.toDisplayString(r(s)),1))),128))]))}}),P={number:M,date:H,text:U,badge:O,array:q},te={class:"flex items-center space-x-4"},ae={class:"font-medium"},oe={class:"font-medium"},le={class:"font-medium"},ne={class:"flex items-center space-x-2"},re=["disabled"],se={class:"flex items-center space-x-1"},ie=["onClick"],ce=["disabled"],A=e.defineComponent({name:"Pagination",__name:"Pagination",props:{page:{},limit:{},total:{},theme:{default:"light"},size:{default:"medium"}},emits:["update:page"],setup(p,{emit:n}){const t=p,r=e.computed(()=>({small:{text:"text-xs",button:"px-2 h-7 text-xs min-w-[28px] flex items-center justify-center",icon:"w-3.5 h-3.5"},medium:{text:"text-sm",button:"px-3 h-9 text-sm min-w-[36px] flex items-center justify-center",icon:"w-4 h-4"},large:{text:"text-base",button:"px-4 h-11 text-base min-w-[44px] flex items-center justify-center",icon:"w-5 h-5"}})[t.size]),a=e.computed(()=>{const{theme:i}=t;return i==="dark"?{container:"bg-gray-900 border-gray-700",text:"text-gray-300",button:"text-gray-300 bg-gray-800 border-gray-600 hover:bg-gray-700",buttonDisabled:"text-gray-500 bg-gray-800",buttonActive:"bg-blue-600 text-white",ellipsis:"text-gray-400"}:i==="light"?{container:"bg-white border-gray-200",text:"text-gray-700",button:"text-gray-700 bg-white border-gray-300 hover:bg-gray-50",buttonDisabled:"text-gray-400 bg-gray-100",buttonActive:"bg-blue-600 text-white",ellipsis:"text-gray-500"}:{container:"bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-700",text:"text-gray-700 dark:text-gray-300",button:"text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700",buttonDisabled:"text-gray-400 dark:text-gray-500 bg-gray-100 dark:bg-gray-800",buttonActive:"bg-blue-600 text-white",ellipsis:"text-gray-500 dark:text-gray-400"}}),c=n,s=e.computed(()=>Math.ceil(t.total/t.limit)),k=e.computed(()=>t.total===0?0:(t.page-1)*t.limit+1),C=e.computed(()=>Math.min(t.page*t.limit,t.total)),x=e.computed(()=>{const i=[];if(s.value<=7)for(let y=1;y<=s.value;y+=1)i.push(y);else{i.push(1),t.page>4&&i.push("...");const y=Math.max(2,t.page-1),E=Math.min(s.value-1,t.page+1);for(let _=y;_<=E;_+=1)_!==1&&_!==s.value&&i.push(_);t.page<s.value-3&&i.push("..."),s.value>1&&i.push(s.value)}return i}),h=i=>{i>=1&&i<=s.value&&i!==t.page&&c("update:page",i)};return(i,g)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["flex items-center justify-between py-3 border-t",a.value.container])},[e.createElementVNode("div",te,[e.createElementVNode("div",{class:e.normalizeClass(["flex items-center text-sm",a.value.text,r.value.text])},[e.createElementVNode("span",null,[g[2]||(g[2]=e.createTextVNode(" Показано ",-1)),e.createElementVNode("span",ae,e.toDisplayString(k.value),1),g[3]||(g[3]=e.createTextVNode(" до ",-1)),e.createElementVNode("span",oe,e.toDisplayString(C.value),1),g[4]||(g[4]=e.createTextVNode(" з ",-1)),e.createElementVNode("span",le,e.toDisplayString(i.total),1),g[5]||(g[5]=e.createTextVNode(" результатів ",-1))])],2)]),e.createElementVNode("div",ne,[e.createElementVNode("button",{onClick:g[0]||(g[0]=y=>h(i.page-1)),disabled:i.page<=1,class:e.normalizeClass(["relative font-medium rounded-md",r.value.button,i.page<=1?a.value.buttonDisabled:a.value.button])},[(e.openBlock(),e.createElementBlock("svg",{class:e.normalizeClass(r.value.icon),fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},g[6]||(g[6]=[e.createElementVNode("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M15 19l-7-7 7-7"},null,-1)]),2))],10,re),e.createElementVNode("div",se,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(x.value,y=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:y},[y==="..."?(e.openBlock(),e.createElementBlock("span",{key:0,class:e.normalizeClass(["font-medium",r.value.button,a.value.ellipsis])}," ... ",2)):(e.openBlock(),e.createElementBlock("button",{key:1,onClick:E=>h(y),class:e.normalizeClass(["relative font-medium rounded-md",r.value.button,y===i.page?a.value.buttonActive:a.value.button])},e.toDisplayString(y),11,ie))],64))),128))]),e.createElementVNode("button",{onClick:g[1]||(g[1]=y=>h(i.page+1)),disabled:i.page>=s.value,class:e.normalizeClass(["relative font-medium rounded-md",r.value.button,i.page>=s.value?a.value.buttonDisabled:a.value.button])},[(e.openBlock(),e.createElementBlock("svg",{class:e.normalizeClass(r.value.icon),fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},g[7]||(g[7]=[e.createElementVNode("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M9 5l7 7-7 7"},null,-1)]),2))],10,ce)])],2))}}),de={key:0,class:"text-center py-8"},me={class:"inline-flex items-center space-x-2"},pe={key:2,class:"overflow-x-auto"},ge={class:"relative w-full overflow-auto"},ue=["checked","indeterminate"],fe=["onClick"],ye={class:"flex items-center justify-between"},be={key:0,class:"ml-1 text-xs"},he=["onClick"],ke=["checked","onChange"],N=e.defineComponent({name:"DataTable",__name:"DataTable",props:{rows:{default:()=>[]},columns:{default:()=>[]},api:{},tableStyle:{default:"min-width: 50rem"},tableClass:{default:"min-w-full divide-y divide-gray-200 dark:divide-gray-700"},size:{default:"medium"},theme:{default:"light"},loading:{type:Boolean,default:!1},selectable:{type:Boolean,default:!1},selectedRows:{default:()=>[]},limit:{default:10},page:{default:1},total:{default:0},showPagination:{type:Boolean,default:!0},sortable:{type:Boolean,default:!1},clickable:{type:Boolean,default:!1}},emits:["update:page","update:selectedRows","row-click"],setup(p,{emit:n}){const t=p,r=n,a=e.ref(t.selectedRows||[]),c=e.ref([]),s=e.ref([]),k=e.ref([]),C=e.ref(!1),x=e.ref(null),h=e.computed(()=>{if(t.api)return s.value;if(t.sortable&&c.value.length>0){const o=[...t.rows];return o.sort((d,l)=>{for(const u of c.value){if(!u.direction)return 0;const m=d[u.field],w=l[u.field];if(m==null)return 1;if(w==null)return-1;let B=0;typeof m=="string"&&typeof w=="string"?B=m.localeCompare(w):m<w?B=-1:m>w?B=1:B=0;const V=u.direction==="desc"?-B:B;if(V!==0)return V}return 0}),o}return t.rows});e.watch(()=>t.selectedRows,o=>{a.value=o||[]});const i=o=>a.value.some(d=>d===o),g=o=>{const d=a.value.findIndex(l=>l===o);d>-1?a.value.splice(d,1):a.value.push(o),r("update:selectedRows",[...a.value])},y=e.computed(()=>h.value.length>0&&a.value.length===h.value.length),E=e.computed(()=>a.value.length>0&&a.value.length<h.value.length),_=()=>{y.value?a.value=[]:a.value=[...h.value],r("update:selectedRows",[...a.value])},Fe=o=>{if(!t.sortable)return;const d=c.value.findIndex(l=>l.field===o);if(d>=0){const l=c.value[d];l.direction==="asc"?l.direction="desc":l.direction==="desc"&&c.value.splice(d,1)}else c.value.unshift({field:o,direction:"asc"});t.api&&z()},Te=o=>{const d=c.value.find(l=>l.field===o);if(!d)return"↑↓";switch(d.direction){case"asc":return"↑";case"desc":return"↓";default:return"↕️"}},R=o=>t.sortable&&o.sortable!==!1,Pe=o=>{t.clickable&&r("row-click",o)},j=e.ref(t.page),K=e.ref(t.limit),I=e.ref(t.total),$=e.ref(1),L=e.ref(10),S=e.ref(0),Q=e.computed(()=>t.api?$.value:j.value),X=e.computed(()=>t.api?L.value:K.value),Y=e.computed(()=>t.api?S.value:I.value),b=e.computed(()=>{const{theme:o}=t;return o==="dark"?{container:"dark",loading:"text-gray-400",error:"text-red-400",thead:"bg-gray-800",th:"text-gray-300",thHover:"hover:bg-gray-700",tbody:"bg-gray-900 divide-gray-700",tr:"hover:bg-gray-800",td:"text-gray-300",empty:"text-gray-400",checkbox:"border-gray-600 bg-gray-700 text-blue-400"}:o==="light"?{container:"",loading:"text-gray-500",error:"text-red-500",thead:"bg-gray-50",th:"text-gray-500",thHover:"hover:bg-gray-100",tbody:"bg-white divide-gray-200",tr:"hover:bg-gray-50",td:"text-gray-900",empty:"text-gray-500",checkbox:"border-gray-300 bg-white text-blue-600"}:{container:"",loading:"text-gray-500 dark:text-gray-400",error:"text-red-500 dark:text-red-400",thead:"bg-gray-50 dark:bg-gray-800",th:"text-gray-500 dark:text-gray-300",thHover:"hover:bg-gray-100 dark:hover:bg-gray-700",tbody:"bg-white dark:bg-gray-900 divide-gray-200 dark:divide-gray-700",tr:"hover:bg-gray-50 dark:hover:bg-gray-800",td:"text-gray-900 dark:text-gray-300",empty:"text-gray-500 dark:text-gray-400",checkbox:"border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-blue-600 dark:text-blue-400"}});e.watch(()=>t.page,o=>{j.value=o}),e.watch(()=>t.limit,o=>{K.value=o}),e.watch(()=>t.total,o=>{I.value=o});const z=async()=>{if(t.api){C.value=!0,x.value=null;try{const o=new URL(t.api,window.location.origin);if(o.searchParams.set("page",Q.value.toString()),o.searchParams.set("limit",X.value.toString()),c.value.length>0){const u=c.value.filter(m=>m.direction).map(m=>`${m.field}:${m.direction}`).join(",");u&&o.searchParams.set("sort",u)}const d=await fetch(o.toString());if(!d.ok)throw new Error(`HTTP error! status: ${d.status}`);const l=await d.json();l.rows&&Array.isArray(l.rows)&&(s.value=l.rows),l.columns&&Array.isArray(l.columns)&&(k.value=l.columns),l.total!==void 0&&(t.api?S.value=l.total:I.value=l.total)}catch(o){x.value=o instanceof Error?o.message:"Failed to fetch data",console.error("Error fetching data:",o)}finally{C.value=!1}}},Ae=o=>{t.api?$.value=o:(j.value=o,r("update:page",o)),t.api&&z()},Re=e.computed(()=>t.showPagination&&Y.value>0),D=e.computed(()=>({small:{header:"px-3 py-2 text-xs",cell:"px-3 py-2 text-xs"},medium:{header:"px-6 py-3 text-xs",cell:"px-6 py-4 text-sm"},large:{header:"px-8 py-4 text-sm",cell:"px-8 py-6 text-base"}})[t.size]);e.onMounted(()=>{t.api&&(t.page&&($.value=t.page),t.limit&&(L.value=t.limit),t.total&&(S.value=t.total),z())}),e.watch(()=>t.api,o=>{o&&($.value=t.page||1,L.value=t.limit||10,S.value=t.total||0,z())});const v=e.computed(()=>t.api?k.value:t.columns),F=e.ref([]),T=e.ref({});e.provide("registerColumn",(o,d)=>{F.value.push(o),d&&o.slotName&&(T.value[o.name]=d)});const je=e.useSlots(),Z=e.computed(()=>{if(F.value.length>0)return console.log("Using slot columns:",F.value),F.value;const o=je.default?.();if(o){console.log("Slot children:",o);const d=o.filter(l=>{const u=l.type?.name==="Column"||l.type==="Column"||l.type&&typeof l.type=="object"&&l.type.name==="Column";return console.log("Checking child:",l.type,"isColumn:",u),u}).map(l=>{console.log("Column slot props:",l.props),console.log("Column slot type:",l.type);const u=l.props||l.componentProps||{},m=u.field||u.name||"",w=u.header||u.label||"",B=l.children&&l.children.body,V={name:m,ua:w,format:u.format||"text",slotName:B?"body":void 0,...u};return B&&V.name&&(T.value[V.name]=l.children.body),console.log("Processed column:",V),V});if(d.length>0)return console.log("Final processed columns from slots:",d),d}return v.value&&v.value.length>0?(console.log("Using table columns from props/API:",v.value),v.value):[]}),Ie=(o="text")=>P[o]||P.text;return(o,d)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["w-full",b.value.container])},[C.value||t.loading?(e.openBlock(),e.createElementBlock("div",de,[e.createElementVNode("div",me,[d[0]||(d[0]=e.createElementVNode("div",{class:"animate-spin rounded-full h-6 w-6 border-b-2 border-blue-600"},null,-1)),e.createElementVNode("span",{class:e.normalizeClass(b.value.loading)},"Loading...",2)])])):x.value?(e.openBlock(),e.createElementBlock("div",{key:1,class:e.normalizeClass(["text-center py-8",b.value.error])},e.toDisplayString(x.value),3)):!t.loading&&h.value.length>0?(e.openBlock(),e.createElementBlock("div",pe,[e.createElementVNode("div",ge,[e.createElementVNode("table",{class:e.normalizeClass(o.tableClass),style:e.normalizeStyle(o.tableStyle)},[e.createElementVNode("thead",{class:e.normalizeClass(b.value.thead)},[e.createElementVNode("tr",null,[o.selectable?(e.openBlock(),e.createElementBlock("th",{key:0,class:e.normalizeClass(["text-left font-medium uppercase tracking-wider",b.value.th,D.value.header,"w-12"])},[e.createElementVNode("input",{type:"checkbox",checked:y.value,indeterminate:E.value,onChange:_,class:e.normalizeClass(["h-4 w-4 focus:ring-blue-500 rounded",b.value.checkbox])},null,42,ue)],2)):e.createCommentVNode("",!0),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(Z.value,l=>(e.openBlock(),e.createElementBlock("th",{key:l.name,class:e.normalizeClass(["text-left font-medium uppercase tracking-wider",b.value.th,D.value.header,l.align?`text-${l.align}`:"",R(l)?["cursor-pointer",b.value.thHover]:""]),onClick:u=>R(l)?Fe(l.name):void 0},[e.createElementVNode("div",ye,[e.createElementVNode("span",null,e.toDisplayString(l.ua||l.header||l.name),1),R(l)?(e.openBlock(),e.createElementBlock("span",be,e.toDisplayString(Te(l.name)),1)):e.createCommentVNode("",!0)])],10,fe))),128))])],2),e.createElementVNode("tbody",{class:e.normalizeClass(b.value.tbody)},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(h.value,(l,u)=>(e.openBlock(),e.createElementBlock("tr",{key:u,class:e.normalizeClass([b.value.tr,t.clickable?"cursor-pointer":""]),onClick:m=>t.clickable?Pe(l):void 0},[o.selectable?(e.openBlock(),e.createElementBlock("td",{key:0,class:e.normalizeClass(["whitespace-nowrap",b.value.td,D.value.cell,"w-12"])},[e.createElementVNode("input",{type:"checkbox",checked:i(l),onChange:m=>g(l),class:e.normalizeClass(["h-4 w-4 focus:ring-blue-500 rounded",b.value.checkbox])},null,42,ke)],2)):e.createCommentVNode("",!0),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(Z.value,m=>(e.openBlock(),e.createElementBlock("td",{key:m.name,class:e.normalizeClass(["whitespace-nowrap",b.value.td,D.value.cell,m.align?`text-${m.align}`:""])},[m.slotName&&T.value[m.name]?(e.openBlock(),e.createBlock(e.resolveDynamicComponent(T.value[m.name]),{key:0,data:l,value:l[m.name]},null,8,["data","value"])):(e.openBlock(),e.createBlock(e.resolveDynamicComponent(Ie(m.format)),{key:1,value:l[m.name]},null,8,["value"]))],2))),128))],10,he))),128))],2)],6)])])):e.createCommentVNode("",!0),Re.value&&!C.value&&!t.loading&&!x.value?(e.openBlock(),e.createBlock(A,{key:3,page:Q.value,limit:X.value,total:Y.value,theme:t.theme,size:t.size,"onUpdate:page":Ae},null,8,["page","limit","total","theme","size"])):e.createCommentVNode("",!0),!C.value&&!t.loading&&!x.value&&h.value.length===0?(e.openBlock(),e.createElementBlock("div",{key:4,class:e.normalizeClass(["text-center py-8",b.value.empty])}," Дані відсутні ",2)):e.createCommentVNode("",!0)],2))}}),xe={style:{display:"none"}},J=e.defineComponent({name:"Column",__name:"Column",props:{field:{},name:{},header:{},label:{},format:{},hidden:{type:Boolean},sortable:{type:Boolean},width:{},align:{},slot:{},slotName:{}},setup(p){const n=p,t=e.useSlots(),r=e.inject("registerColumn");return e.onMounted(()=>{if(r){const{field:a,name:c,header:s,label:k,format:C,slotName:x,...h}=n,E={name:a||c||"",ua:s||k||"",format:C||"text",slotName:x||(t.body?"body":void 0),...h};console.log("Registering column:",E);const _=t.body;r(E,_)}}),(a,c)=>(e.openBlock(),e.createElementBlock("div",xe,[e.renderSlot(a.$slots,"body",{data:{}})]))}}),Ce={class:"mt-4"},_e={class:"text-sm text-gray-600 dark:text-gray-300"},W=e.defineComponent({__name:"SelectionInfo",props:{selectedRows:{},totalCount:{}},setup(p){const n=p,t=e.computed(()=>n.selectedRows.length);return(r,a)=>(e.openBlock(),e.createElementBlock("div",Ce,[e.createElementVNode("div",_e," Обрано: "+e.toDisplayString(t.value)+" з "+e.toDisplayString(r.totalCount)+" записів ",1)]))}}),Be={class:"mt-4"},we={class:"border-b border-gray-200 dark:border-gray-700"},Ee={class:"-mb-px flex space-x-8"},Ve=["onClick"],Ne={class:"mt-4"},$e={class:"bg-gray-900 rounded-lg overflow-hidden"},Se={class:"flex items-center justify-between px-4 py-2 bg-gray-800 border-b border-gray-700"},ze={class:"text-sm text-gray-300"},De=["onClick"],ve={class:"p-4 text-sm text-gray-100 overflow-x-auto"},G=e.defineComponent({__name:"CodeTabs",props:{tabs:{},defaultTab:{default:void 0}},setup(p){const n=p,t=e.ref(n.defaultTab||n.tabs[0]?.id||""),r=async a=>{try{await navigator.clipboard.writeText(a),console.log("Код скопійовано!")}catch(c){console.error("Помилка копіювання:",c)}};return(a,c)=>(e.openBlock(),e.createElementBlock("div",Be,[e.createElementVNode("div",we,[e.createElementVNode("nav",Ee,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(a.tabs,s=>(e.openBlock(),e.createElementBlock("button",{key:s.id,onClick:k=>t.value=s.id,class:e.normalizeClass(["py-2 px-1 border-b-2 font-medium text-sm transition-colors",t.value===s.id?"border-blue-500 text-blue-600 dark:text-blue-400":"border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300"])},e.toDisplayString(s.label),11,Ve))),128))])]),e.createElementVNode("div",Ne,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(a.tabs,s=>e.withDirectives((e.openBlock(),e.createElementBlock("div",{key:s.id},[e.createElementVNode("div",$e,[e.createElementVNode("div",Se,[e.createElementVNode("span",ze,e.toDisplayString(s.label),1),e.createElementVNode("button",{onClick:k=>r(s.content),class:"text-gray-400 hover:text-white transition-colors",title:"Копіювати код"},c[0]||(c[0]=[e.createElementVNode("svg",{class:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[e.createElementVNode("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"})],-1)]),8,De)]),e.createElementVNode("pre",ve,[e.createElementVNode("code",null,e.toDisplayString(s.content),1)])])])),[[e.vShow,t.value===s.id]])),128))])]))}});N.install=function(n){n.component("DataTable",N),n.component("Column",J),n.component("Pagination",A),n.component("SelectionInfo",W),n.component("CodeTabs",G)},f.ArrayFormat=q,f.BadgeFormat=O,f.CodeTabs=G,f.Column=J,f.DataTable=N,f.DateFormat=H,f.NumberFormat=M,f.Pagination=A,f.SelectionInfo=W,f.TextFormat=U,f.default=N,f.formatComponents=P,Object.defineProperties(f,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@opengis/table",
3
+ "version": "0.0.1",
4
+ "description": "A Vue 3 data table component with advanced features",
5
+ "main": "dist/index.mjs",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "keywords": [
10
+ "vue",
11
+ "vue3",
12
+ "table",
13
+ "data-table",
14
+ "component",
15
+ "typescript"
16
+ ],
17
+ "license": "MIT",
18
+ "peerDependencies": {
19
+ "vue": "^3.0.0"
20
+ },
21
+ "devDependencies": {
22
+ "@tsconfig/node22": "^22.0.2",
23
+ "@types/node": "^22.15.32",
24
+ "@vitejs/plugin-vue": "^6.0.0",
25
+ "@vue/tsconfig": "^0.7.0",
26
+ "typescript": "~5.8.0",
27
+ "vite": "^7.0.0",
28
+ "vue-tsc": "^2.2.10",
29
+ "eslint": "8.49.0",
30
+ "eslint-config-airbnb": "19.0.4",
31
+ "eslint-plugin-import": "^2.25.3",
32
+ "eslint-plugin-vue": "^9.17.0",
33
+ "@vue/eslint-config-typescript": "^12.0.0",
34
+ "vue": "^3.5.17"
35
+ },
36
+ "scripts": {
37
+ "lint": "eslint src --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
38
+ "dev": "vite",
39
+ "build": "vite build",
40
+ "docs": "vite docs",
41
+ "build:lib": "vite build --config vite.lib.config.ts",
42
+ "build:types": "copy src\\types\\index.ts dist\\types.d.ts",
43
+ "build:full": "npm run build:lib && npm run build:types",
44
+ "preview": "vite preview",
45
+ "build-only": "vite build",
46
+ "prepublishOnly": "bun run build ",
47
+ "docs:dev": "npm run --prefix ./docs docs:dev",
48
+ "docs:build": "npm run --prefix ./docs docs:build",
49
+ "docs:preview": "npm run --prefix ./docs docs:preview"
50
+ },
51
+ "directories": {
52
+ "example": "examples"
53
+ },
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "https://git.softpro.ua/npm/table.git"
57
+ },
58
+ "author": "softpro"
59
+ }