flameresttable 1.0.118 → 1.0.122

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/package.json CHANGED
@@ -1,49 +1,49 @@
1
1
  {
2
2
  "name": "flameresttable",
3
- "version": "1.0.118",
3
+ "version": "1.0.122",
4
4
  "main": "./src/plugin.ts",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/FlameArt/resttable.git"
8
8
  },
9
9
  "scripts": {
10
- "push and publish": "npm --no-git-tag-version version patch && npm publish",
10
+ "publish": "npm --no-git-tag-version version patch && npm publish",
11
11
  "install/update": "npm i && npm update --save",
12
12
  "dev": "vite"
13
13
  },
14
14
  "dependencies": {
15
- "@vuepic/vue-datepicker": "^11.0.2",
16
- "flamerest": "^1.0.125",
17
- "lodash-es": "^4.17.21",
15
+ "@vuepic/vue-datepicker": "^11.0.3",
16
+ "flamerest": "file:../REST JS",
17
+ "lodash-es": "^4.17.23",
18
18
  "pinia": "^2.3.1",
19
19
  "pug": "github:FlameArt/pug-tailwind-only",
20
20
  "pug-lexer": "github:FlameArt/pug-lexer-only",
21
- "sass": "^1.89.2",
21
+ "sass": "^1.97.3",
22
22
  "vue": "^3.5.13",
23
- "vue-router": "^4.5.1",
23
+ "vue-router": "^4.6.4",
24
24
  "vue-select": "^4.0.0-beta.6",
25
- "vuetify": "^3.8.12",
26
- "vuetify-pro-tiptap": "^2.6.0"
25
+ "vuetify": "^3.11.8",
26
+ "vuetify-pro-tiptap": "^2.8.2"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@heroicons/vue": "^2.2.0",
30
30
  "@types/lodash-es": "^4.17.12",
31
31
  "@types/lodash.merge": "^4.6.9",
32
- "@types/node": "^22.16.0",
32
+ "@types/node": "^22.19.7",
33
33
  "@types/vue-select": "^3.16.8",
34
- "@typescript-eslint/eslint-plugin": "^8.35.1",
35
- "@typescript-eslint/parser": "^8.35.1",
34
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
35
+ "@typescript-eslint/parser": "^8.54.0",
36
36
  "@vitejs/plugin-vue": "^5.2.4",
37
37
  "@volar/vue-language-plugin-pug": "^1.6.5",
38
38
  "@vue/compiler-sfc": "^3.5.13",
39
- "autoprefixer": "^10.4.21",
40
- "eslint": "^9.30.1",
39
+ "autoprefixer": "^10.4.24",
40
+ "eslint": "^9.39.2",
41
41
  "eslint-plugin-vue": "^9.33.0",
42
42
  "postcss": "^8.5.6",
43
43
  "postcss-import": "^16.1.1",
44
- "tailwindcss": "^3.4.17",
45
- "typescript": "^5.8.3",
46
- "vite": "^6.3.5",
44
+ "tailwindcss": "^3.4.19",
45
+ "typescript": "^5.9.3",
46
+ "vite": "^6.4.1",
47
47
  "vue-tsc": "^2.2.12",
48
48
  "vue-universal-modal": "^1.1.4"
49
49
  },
package/src/App.vue CHANGED
@@ -1,5 +1,20 @@
1
+ <template>
2
+ <v-app>
3
+ <v-main>
4
+ <Table :model="Model" :opts="opts">
5
+ <template v-slot:RowSubSlot="params">
6
+ <!-- {{ params.row.name + ' LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG line' }} -->
7
+ <!-- <SubRow /> -->
8
+ <div class="flex items-center justify-center w-full mx-auto">AAA</div>
9
+ </template>
10
+ <template #otherButtons></template>
11
+ </Table>
12
+ </v-main>
13
+ </v-app>
14
+ </template>
15
+
1
16
  <script setup lang="ts">
2
- import { onMounted, reactive, defineProps } from '@vue/runtime-core'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest"
17
+ import { onMounted, reactive } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest"
3
18
  import Table from './Table/Table.vue';
4
19
  import { ref } from 'vue';
5
20
  import Model from '@models/Testtable';
@@ -55,26 +70,10 @@ opts.LoadParams.sort = ['-dt1'];
55
70
 
56
71
  </script>
57
72
 
58
- <template lang="pug">
59
- v-app
60
- v-main
61
- Table(:model="Model", :opts="opts")
62
- template(v-slot:RowSubSlot="params")
63
- //| {{ params.row.name + ' LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG line' }}
64
- //SubRow
65
- div(class="flex items-center justify-center w-full mx-auto") AAA
66
- template(#otherButtons)
67
- </template>
68
-
69
73
  <style>
70
74
  #app {
71
75
  font-family: Avenir, Helvetica, Arial, sans-serif;
72
76
  -webkit-font-smoothing: antialiased;
73
77
  -moz-osx-font-smoothing: grayscale;
74
78
  }
75
-
76
- /* Цвет стандартного фона модального окна */
77
- .vue-universal-modal {
78
- background-color: rgba(0, 0, 0, 0.15) !important;
79
- }
80
79
  </style>
@@ -30,6 +30,11 @@ export class Column implements IColumn {
30
30
  */
31
31
  isShow: true,
32
32
 
33
+ /**
34
+ * Позиция колонки для сортировки
35
+ */
36
+ position: 999,
37
+
33
38
  /**
34
39
  * Заголовок столбца в таблице
35
40
  */
@@ -118,13 +123,23 @@ export class Column implements IColumn {
118
123
  */
119
124
  values: [] as ITableSelectorItem[],
120
125
 
126
+ /**
127
+ * Разрешить выбирать пустое значение (null)
128
+ */
129
+ allowNull: true,
130
+
131
+ /**
132
+ * Заголовок для пустого значения
133
+ */
134
+ nullTitle: "—",
135
+
121
136
  /**
122
137
  * Кастомизация вывода отдельного элемента
123
138
  * @param row
124
139
  * @param col
125
140
  * @returns
126
141
  */
127
- value: (row: any, col: string) => row[col]
142
+ value: (row: any, col: string) => row[col],
128
143
 
129
144
  };
130
145
 
@@ -158,9 +173,15 @@ export class Column implements IColumn {
158
173
  // Минимальное число символов в строке фильтра, после которого будет произведён запрос
159
174
  filterMinSymbolsRequest: 0,
160
175
 
161
- // Позиция фильтра по порядку
162
- filterRow: 0,
163
- filterColumn: 0,
176
+ /**
177
+ * Позиция фильтра в общей очереди
178
+ */
179
+ position: 999,
180
+
181
+ /**
182
+ * Ширина фильтра в пикселях
183
+ */
184
+ width: null as number | null,
164
185
 
165
186
  /**
166
187
  * CSS-классы,
@@ -182,7 +203,7 @@ export class Column implements IColumn {
182
203
  public Popup = {
183
204
 
184
205
  // Тут привязка к реальным данным
185
- model: "",
206
+ model: "" as any,
186
207
 
187
208
  // Тут модель для файлов
188
209
  fileModel: null as any,
@@ -225,10 +246,20 @@ export class Column implements IColumn {
225
246
  Selector: {
226
247
 
227
248
  // Значения селектора - готовые
228
- values: [],
249
+ values: [] as ITableSelectorItem[],
250
+
251
+ /**
252
+ * Разрешить выбирать пустое значение (null)
253
+ */
254
+ allowNull: false,
255
+
256
+ /**
257
+ * Заголовок для пустого значения
258
+ */
259
+ nullTitle: "—",
229
260
 
230
261
  // Загрузчик значений - используется всегда и перекрывает values
231
- loader: (column: Column) => null
262
+ loader: (column: Column) => null as ITableSelectorItem[] | null,
232
263
 
233
264
  },
234
265
 
@@ -318,6 +349,11 @@ export interface IColumn {
318
349
  width?: number | null,
319
350
  isSelected?: boolean,
320
351
 
352
+ /**
353
+ * Позиция колонки для сортировки
354
+ */
355
+ position?: number,
356
+
321
357
  /**
322
358
  * Кастомный заголовок
323
359
  * @param row
@@ -350,9 +386,15 @@ export interface IColumn {
350
386
  // Минимальное число символов в строке фильтра, после которого будет произведён запрос
351
387
  filterMinSymbolsRequest?: number,
352
388
 
353
- // Позиция фильтра по порядку
354
- filterRow?: number,
355
- filterColumn?: number,
389
+ /**
390
+ * Позиция фильтра в общей очереди
391
+ */
392
+ position?: number,
393
+
394
+ /**
395
+ * Ширина фильтра в пикселях
396
+ */
397
+ width?: number | null,
356
398
 
357
399
  /**
358
400
  * Список значений под выбор
@@ -405,6 +447,16 @@ export interface IColumn {
405
447
  */
406
448
  values?: ITableSelectorItem[],
407
449
 
450
+ /**
451
+ * Разрешить выбирать пустое значение (null)
452
+ */
453
+ allowNull?: boolean,
454
+
455
+ /**
456
+ * Заголовок для пустого значения
457
+ */
458
+ nullTitle?: string,
459
+
408
460
 
409
461
  /**
410
462
  * Модель, с которой выгружать значения
@@ -418,7 +470,7 @@ export interface IColumn {
418
470
  * @param col
419
471
  * @returns
420
472
  */
421
- value?: (row: any, col: string) => any
473
+ value?: (row: any, col: string) => any,
422
474
 
423
475
  },
424
476
 
@@ -466,15 +518,24 @@ export interface IColumn {
466
518
  // Текст ошибки добавления/обновления
467
519
  errorMessage?: string,
468
520
 
469
-
470
521
  // Настройки селектора, если он включён в popupType
471
522
  Selector?: {
472
523
 
473
524
  // Значения селектора - готовые
474
- values?: Array<any>,
525
+ values?: ITableSelectorItem[],
526
+
527
+ /**
528
+ * Разрешить выбирать пустое значение (null)
529
+ */
530
+ allowNull?: boolean,
531
+
532
+ /**
533
+ * Заголовок для пустого значения
534
+ */
535
+ nullTitle?: string,
475
536
 
476
537
  // Загрузчик значений - используется всегда и перекрывает values
477
- loader?: (column: Column) => Array<any> | null
538
+ loader?: (column: Column) => ITableSelectorItem[] | null,
478
539
 
479
540
  },
480
541
 
@@ -94,11 +94,12 @@ export default class FlameTable<T> {
94
94
  // удалим так же дубликаты
95
95
  // TODO: as any
96
96
  const allColumns = [...Object.keys(this.model as any), ...Object.keys(opts.columnsOpts)].filter((value, index, self) => self.indexOf(value) === index)
97
+ const tempColumns: Column[] = [];
97
98
 
98
99
  // генерим полноценные колонки из описания модели
99
100
  for (const key of allColumns) {
100
101
  const newCol = new Column;
101
- this.columns[key] = newCol;
102
+ //this.columns[key] = newCol;
102
103
 
103
104
  // Выключаем из редактирования все праймари ключи
104
105
  if ((this.model as any).constructor['primaryKeys'].includes(key))
@@ -111,14 +112,28 @@ export default class FlameTable<T> {
111
112
  newCol.name = key;
112
113
 
113
114
  // Загружаем селекторы один раз за страницу
114
- if (newCol.Popup.popupType === 'selector' && newCol.Popup.Selector.loader !== null)
115
+ if (newCol.Popup.popupType === 'selector' && newCol.Popup.Selector.loader !== null) {
115
116
  newCol.Popup.Selector.values = reactive(newCol.Popup.Selector.loader(newCol) ?? newCol.Popup.Selector.values)
117
+ }
118
+
119
+ tempColumns.push(newCol);
120
+
121
+ }
122
+
123
+ // Сортируем колонки по их позиции
124
+ tempColumns.sort((a, b) => (a.Table.position ?? 999) - (b.Table.position ?? 999));
116
125
 
126
+ // Заполняем итоговый объект в правильном порядке
127
+ for (const col of tempColumns) {
128
+ this.columns[col.name] = col;
117
129
  }
118
130
 
119
131
  // Мержим параметры с базовой версией
120
132
  merge(this.opts, opts);
121
133
 
134
+ // Устанавливаем ссылку на таблицу для обратного вызова
135
+ (this.opts as any)._tableInstance = this;
136
+
122
137
  // TODO: Сортируем колонки, если указан порядок
123
138
  //this.columns
124
139
 
@@ -127,7 +142,8 @@ export default class FlameTable<T> {
127
142
  // Предзагружаем колонки
128
143
  this.preloadRelated()
129
144
 
130
-
145
+ // Инициализируем null-элементы для селекторов
146
+ this.initSelectorNulls()
131
147
 
132
148
  }
133
149
 
@@ -196,6 +212,7 @@ export default class FlameTable<T> {
196
212
  col.Selector.values.push(...col.Selector.loader(r.data))
197
213
  }
198
214
 
215
+
199
216
  // Преобразования не найдено, попытка автоматически определить параметры
200
217
  if (col.Selector.loader === null) {
201
218
  const rows = r.data;
@@ -227,6 +244,59 @@ export default class FlameTable<T> {
227
244
 
228
245
  }
229
246
 
247
+ /**
248
+ * Переинициализирует селекторы для указанной колонки
249
+ */
250
+ public reinitSelectorForColumn(columnName: string) {
251
+ if (!this.columns[columnName]) return
252
+
253
+ const col = this.columns[columnName]
254
+
255
+ // Для popup selector
256
+ if (col.Popup.popupType === 'selector') {
257
+ // Удаляем существующий null-элемент если есть
258
+ col.Popup.Selector.values = col.Popup.Selector.values.filter(v => v.id !== null)
259
+
260
+ // Добавляем null только если allowNull = true
261
+ if (col.Popup.Selector.allowNull) {
262
+ col.Popup.Selector.values.unshift({
263
+ id: null,
264
+ title: col.Popup.Selector.nullTitle,
265
+ label: col.Popup.Selector.nullTitle,
266
+ })
267
+ }
268
+ }
269
+
270
+ // Для обычного селектора (фильтры)
271
+ if (col.Selector.allowNull) {
272
+ // Удаляем существующий null-элемент если есть
273
+ col.Selector.values = col.Selector.values.filter(v => v.id !== null)
274
+
275
+ col.Selector.values.unshift({
276
+ id: null,
277
+ title: col.Selector.nullTitle,
278
+ label: col.Selector.nullTitle,
279
+ position: -1
280
+ })
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Инициализирует null-элементы для всех селекторов, где allowNull = true
286
+ */
287
+ private initSelectorNulls() {
288
+ for (const key in this.columns) {
289
+ this.reinitSelectorForColumn(key)
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Переинициализирует все селекторы (можно вызывать после изменения данных в лоадерах)
295
+ */
296
+ public reinitSelectors() {
297
+ this.initSelectorNulls()
298
+ }
299
+
230
300
  /**
231
301
  * Обновить результаты от RESTа
232
302
  * @param CustomLoadParams Применить кастомные параметры загрузки
@@ -234,6 +304,17 @@ export default class FlameTable<T> {
234
304
  */
235
305
  public async update(CustomLoadParams: ITableLoadParams = {}, exportFilename: string | null = null, from: 'pager' | 'filters' = 'pager') {
236
306
 
307
+ if (from !== 'pager') {
308
+ this.Pager.page = 1;
309
+
310
+ // Сбрасываем выделенные строки
311
+ for (const key in this.RowsParams) {
312
+ this.RowsParams[key].selected = false;
313
+ }
314
+ this.RowsSelected.splice(0);
315
+
316
+ }
317
+
237
318
  // Дополняем загрузочные параметры фильтрами
238
319
  const filters = merge(this.applyFiltersParams(), CustomLoadParams);
239
320
 
@@ -335,7 +416,7 @@ export default class FlameTable<T> {
335
416
  // Частичное совпадение
336
417
  case 'text':
337
418
 
338
- if (el.valueString.trim() === '') continue;
419
+ if (el.valueString === null || el.valueString.trim() === '') continue;
339
420
  customFilters.where[key] = ['LIKE', key, el.valueString];
340
421
 
341
422
  break;
@@ -343,7 +424,7 @@ export default class FlameTable<T> {
343
424
  // Фуллтекстовый поиск
344
425
  case 'fulltext':
345
426
 
346
- if (el.valueString.trim() === '') continue;
427
+ if (el.valueString === null || el.valueString.trim() === '') continue;
347
428
  customFilters.where[key] = ['FULLTEXT', key, el.valueString];
348
429
  break;
349
430
 
@@ -351,7 +432,7 @@ export default class FlameTable<T> {
351
432
  case 'fixed':
352
433
  case 'date':
353
434
 
354
- if (el.valueString.trim() === '') continue;
435
+ if (el.valueString === null || el.valueString.trim() === '') continue;
355
436
  customFilters.where[key] = el.valueString;
356
437
 
357
438
  break;
@@ -384,8 +465,10 @@ export default class FlameTable<T> {
384
465
  if (el.valueRange.length > 0)
385
466
  customFilters.where[key] = ['IN', key, el.valueRange];
386
467
  }
387
- else
388
- customFilters.where[key] = el.valueString;
468
+ else {
469
+ if (el.valueString !== "")
470
+ customFilters.where[key] = el.valueString;
471
+ }
389
472
 
390
473
  break;
391
474
 
@@ -401,7 +484,7 @@ export default class FlameTable<T> {
401
484
 
402
485
  // Сперва предобработка
403
486
  // TODO: убрать пустой массив т.к. при добавлении он не нужен или прикрепить реактивную модель?
404
- if (await this.opts.Popup.beforeAdd({}, this) === false) { return null; }
487
+ if (await this.opts.Popup.beforeAdd({}, this as any) === false) { return null; }
405
488
 
406
489
  const cols = await (window as any).REST.prepare(this.getColumnsForUpdate('add'), true);
407
490
  const res: SavedObject<T> = await Object.getPrototypeOf(this.model).constructor.create(cols);
@@ -423,7 +506,7 @@ export default class FlameTable<T> {
423
506
  const indexKey = tClass.primaryKeys[0];
424
507
 
425
508
  // Сперва предобработка
426
- if (await this.opts.Popup.beforeEdit(columns, this) === false) { return null; }
509
+ if (await this.opts.Popup.beforeEdit(columns, this as any) === false) { return null; }
427
510
 
428
511
  // Убираем поля, которые не требуют изменения
429
512
  const cleanedCols = this.RemoveUnchagedFields(columns);
@@ -456,7 +539,7 @@ export default class FlameTable<T> {
456
539
 
457
540
  }
458
541
 
459
- private RemoveUnchagedFields(row: { [key: string]: any }) {
542
+ public RemoveUnchagedFields(row: { [key: string]: any }) {
460
543
 
461
544
  // primarykey
462
545
  const pk = (this.model as any).constructor.primaryKeys[0];
@@ -507,7 +590,7 @@ export default class FlameTable<T> {
507
590
 
508
591
  }
509
592
 
510
- private getColumnsForUpdate(mode: "add" | "edit") {
593
+ public getColumnsForUpdate(mode: "add" | "edit") {
511
594
  const res: { [key: string]: string } = {};
512
595
  for (const key in this.columns) {
513
596
 
@@ -1,25 +1,43 @@
1
- <template lang="pug">
2
- nav.relative.z-0.w-full.fc.flex-wrap.rounded-md.-space-x-px(aria-label='Pagination' @click="")
3
- a.relative.inline-flex.items-center.px-2.py-2.rounded-l-md.border.border-gray-300.bg-white.text-sm.font-medium.text-gray-500(@click="goPage(props.table.Pager.page-1)" :class="props.table.Pager.page === 1 ? 'opacity-30' : 'cursor-pointer'" class='hover:bg-gray-50')
4
- span.sr-only Previous
5
- // Heroicon name: solid/chevron-left
6
- svg.h-5.w-5(xmlns='http://www.w3.org/2000/svg' viewbox='0 0 20 20' fill='currentColor' aria-hidden='true')
7
- path(fill-rule='evenodd' d='M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z' clip-rule='evenodd')
8
- // Current: "z-10 bg-indigo-50 border-indigo-500 text-indigo-600", Default: "bg-white border-gray-300 text-gray-500 hover:bg-gray-50"
9
- a.cursor-pointer.bg-white.border-gray-300.text-gray-500.relative.inline-flex.items-center.px-4.py-2.border.text-sm.font-medium(v-for="page in props.table.Pager.count" class="hover:bg-gray-50" :class="props.table.Pager.page === page ? 'selectedPage' : ''" @click="goPage(page)")
10
- | {{ page }}
11
- //span.relative.inline-flex.items-center.px-4.py-2.border.border-gray-300.bg-white.text-sm.font-medium.text-gray-700 ...
12
- a.relative.inline-flex.items-center.px-2.py-2.rounded-r-md.border.border-gray-300.bg-white.text-sm.font-medium.text-gray-500(@click="goPage(props.table.Pager.page+1)" :class="props.table.Pager.page >= props.table.Pager.count ? 'opacity-30' : 'cursor-pointer'" class='hover:bg-gray-50')
13
- span.sr-only Next
14
- // Heroicon name: solid/chevron-right
15
- svg.h-5.w-5(xmlns='http://www.w3.org/2000/svg' viewbox='0 0 20 20' fill='currentColor' aria-hidden='true')
16
- path(fill-rule='evenodd' d='M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z' clip-rule='evenodd')
17
-
18
-
1
+ <template>
2
+ <nav class="relative z-0 w-full fc flex-wrap rounded-md -space-x-px" aria-label="Pagination" @click="">
3
+ <a
4
+ class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"
5
+ @click="goPage(props.table.Pager.page - 1)"
6
+ :class="props.table.Pager.page === 1 ? 'opacity-30' : 'cursor-pointer'"
7
+ >
8
+ <span class="sr-only">Previous</span>
9
+ <!-- Heroicon name: solid/chevron-left -->
10
+ <svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
11
+ <path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd"></path>
12
+ </svg>
13
+ </a>
14
+ <!-- Current: "z-10 bg-indigo-50 border-indigo-500 text-indigo-600", Default: "bg-white border-gray-300 text-gray-500 hover:bg-gray-50" -->
15
+ <a
16
+ class="cursor-pointer bg-white border-gray-300 text-gray-500 relative inline-flex items-center px-4 py-2 border text-sm font-medium hover:bg-gray-50"
17
+ v-for="page in props.table.Pager.count"
18
+ :key="page"
19
+ :class="props.table.Pager.page === page ? 'selectedPage' : ''"
20
+ @click="goPage(page)"
21
+ >
22
+ {{ page }}
23
+ </a>
24
+ <!-- <span class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700">...</span> -->
25
+ <a
26
+ class="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"
27
+ @click="goPage(props.table.Pager.page + 1)"
28
+ :class="props.table.Pager.page >= props.table.Pager.count ? 'opacity-30' : 'cursor-pointer'"
29
+ >
30
+ <span class="sr-only">Next</span>
31
+ <!-- Heroicon name: solid/chevron-right -->
32
+ <svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
33
+ <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
34
+ </svg>
35
+ </a>
36
+ </nav>
19
37
  </template>
20
38
 
21
39
  <script setup lang="ts">
22
- import { onMounted, reactive, ref, defineProps } from '@vue/runtime-core'; import type { Ref } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest"
40
+ import { onMounted, reactive, ref } from 'vue'; import type { Ref } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest"
23
41
 
24
42
  // Иконки
25
43
  import { XCircleIcon } from '@icons/24/solid'