grav-svelte 0.0.62 → 0.0.63

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 (42) hide show
  1. package/dist/CRUD/CrudTable.svelte +62 -1796
  2. package/dist/CRUD/CrudTable.svelte.d.ts +2 -1
  3. package/dist/CRUD/cells/BoolCell.svelte +26 -0
  4. package/dist/CRUD/cells/BoolCell.svelte.d.ts +18 -0
  5. package/dist/CRUD/cells/CellRenderer.svelte +65 -0
  6. package/dist/CRUD/cells/CellRenderer.svelte.d.ts +23 -0
  7. package/dist/CRUD/cells/ConditionalCell.svelte +50 -0
  8. package/dist/CRUD/cells/ConditionalCell.svelte.d.ts +19 -0
  9. package/dist/CRUD/cells/DateCell.svelte +20 -0
  10. package/dist/CRUD/cells/DateCell.svelte.d.ts +19 -0
  11. package/dist/CRUD/cells/DualTextButtonCell.svelte +28 -0
  12. package/dist/CRUD/cells/DualTextButtonCell.svelte.d.ts +19 -0
  13. package/dist/CRUD/cells/DynamicButtonCell.svelte +31 -0
  14. package/dist/CRUD/cells/DynamicButtonCell.svelte.d.ts +19 -0
  15. package/dist/CRUD/cells/EditableBoolCell.svelte +31 -0
  16. package/dist/CRUD/cells/EditableBoolCell.svelte.d.ts +19 -0
  17. package/dist/CRUD/cells/EditableNumberCell.svelte +34 -0
  18. package/dist/CRUD/cells/EditableNumberCell.svelte.d.ts +19 -0
  19. package/dist/CRUD/cells/EditableTextCell.svelte +29 -0
  20. package/dist/CRUD/cells/EditableTextCell.svelte.d.ts +19 -0
  21. package/dist/CRUD/cells/ImageButtonCell.svelte +23 -0
  22. package/dist/CRUD/cells/ImageButtonCell.svelte.d.ts +19 -0
  23. package/dist/CRUD/cells/ImageCell.svelte +14 -0
  24. package/dist/CRUD/cells/ImageCell.svelte.d.ts +19 -0
  25. package/dist/CRUD/cells/MultiTextButtonCell.svelte +26 -0
  26. package/dist/CRUD/cells/MultiTextButtonCell.svelte.d.ts +19 -0
  27. package/dist/CRUD/cells/TextCell.svelte +15 -0
  28. package/dist/CRUD/cells/TextCell.svelte.d.ts +18 -0
  29. package/dist/CRUD/components/SubRowsTable.svelte +29 -0
  30. package/dist/CRUD/components/SubRowsTable.svelte.d.ts +20 -0
  31. package/dist/CRUD/components/TableHeader.svelte +112 -0
  32. package/dist/CRUD/components/TableHeader.svelte.d.ts +22 -0
  33. package/dist/CRUD/components/TableRow.svelte +106 -0
  34. package/dist/CRUD/components/TableRow.svelte.d.ts +36 -0
  35. package/dist/CRUD/composables/useDragAndDrop.d.ts +19 -0
  36. package/dist/CRUD/composables/useDragAndDrop.js +82 -0
  37. package/dist/CRUD/composables/useTableExpand.d.ts +4 -0
  38. package/dist/CRUD/composables/useTableExpand.js +20 -0
  39. package/dist/CRUD/composables/useTableSort.d.ts +5 -0
  40. package/dist/CRUD/composables/useTableSort.js +16 -0
  41. package/dist/CRUD/styles/CrudTable.css +661 -0
  42. package/package.json +1 -1
@@ -1,13 +1,15 @@
1
1
  <script lang="ts">
2
- import CrudTableButtons from "./CrudTableButtons.svelte";
3
2
  import { openModal, closeModal } from "../Modals/index.js";
4
3
  import ImageModal from "./ImageModal.svelte";
5
-
6
- // COMPONENTES imports
7
-
8
4
  import { createEventDispatcher } from "svelte";
9
5
  import type { TableHeader } from "./interfaces.js";
6
+ import TableHeaderComponent from "./components/TableHeader.svelte";
7
+ import TableRow from "./components/TableRow.svelte";
8
+ import SubRowsTable from "./components/SubRowsTable.svelte";
9
+ import { useTableSort } from "./composables/useTableSort.js";
10
+ import { useTableExpand } from "./composables/useTableExpand.js";
10
11
  import "../typography.css";
12
+ import "./styles/CrudTable.css";
11
13
 
12
14
  const dispatch = createEventDispatcher();
13
15
 
@@ -24,36 +26,37 @@
24
26
  export let subRowsField: string = "subRows";
25
27
  export let subRowHeaders: TableHeader[] | undefined = undefined;
26
28
 
27
- let selectedAscOrDesc = "asc";
28
- let selectedSort = "";
29
29
  let selectedRowId: string | number | null = null;
30
- let expandedRows = new Set<string | number>();
31
30
 
32
31
  // Use custom subRowHeaders if provided, otherwise use parent headers
33
32
  $: effectiveSubRowHeaders = subRowHeaders || tableHeaders;
34
33
 
35
- // Drag and drop variables
34
+ // Drag and drop state
36
35
  let draggedIndex: number | null = null;
37
36
  let dragOverIndex: number | null = null;
38
37
  let isDragging = false;
39
- let reorderedItems: any[] = [];
38
+
39
+ // Sort composable
40
+ const sortComposable = useTableSort((field: string, direction: "asc" | "desc") => {
41
+ dispatch("selectedSort", {
42
+ selectedSort: field,
43
+ selectedAsc: direction,
44
+ });
45
+ });
46
+
47
+ const { selectedAscOrDesc, selectedSort, dispatchSort } = sortComposable;
48
+
49
+ // Expand composable
50
+ const expandComposable = useTableExpand();
51
+ const { expandedRows, toggleExpand } = expandComposable;
40
52
 
41
53
  function handleRowClick(id: string | number) {
42
54
  selectedRowId = selectedRowId === id ? null : id;
43
55
  dispatch("rowClick", { id: selectedRowId });
44
56
  }
45
57
 
46
- function dispatchSort(selection: string) {
47
- if (selectedAscOrDesc == "asc") {
48
- selectedAscOrDesc = "desc";
49
- } else {
50
- selectedAscOrDesc = "asc";
51
- }
52
- selectedSort = selection;
53
- dispatch("selectedSort", {
54
- selectedSort: selection,
55
- selectedAsc: selectedAscOrDesc,
56
- });
58
+ function handleSort(field: string) {
59
+ dispatchSort(field, $selectedAscOrDesc);
57
60
  }
58
61
 
59
62
  function openImageModal(src: string) {
@@ -61,24 +64,9 @@
61
64
  openModal("crud-image-modal", ImageModal, { src });
62
65
  }
63
66
 
64
- function toggleExpand(id: string | number) {
65
- const newExpandedRows = new Set(expandedRows);
66
- if (newExpandedRows.has(id)) {
67
- newExpandedRows.delete(id);
68
- } else {
69
- newExpandedRows.add(id);
70
- }
71
- expandedRows = newExpandedRows;
72
- }
73
-
74
67
  // Drag and drop functions
75
68
  function handleDragStart(event: DragEvent, index: number) {
76
- if (
77
- !dragEnabled ||
78
- !todosLosRegistros ||
79
- todosLosRegistros.length === 0 ||
80
- loading
81
- ) {
69
+ if (!dragEnabled || !todosLosRegistros || todosLosRegistros.length === 0 || loading) {
82
70
  event.preventDefault();
83
71
  return;
84
72
  }
@@ -108,16 +96,11 @@
108
96
  }
109
97
 
110
98
  function handleDrop(event: DragEvent, dropIndex: number) {
111
- if (
112
- !dragEnabled ||
113
- draggedIndex === null ||
114
- draggedIndex === dropIndex
115
- ) {
99
+ if (!dragEnabled || draggedIndex === null || draggedIndex === dropIndex) {
116
100
  resetDragState();
117
101
  return;
118
102
  }
119
103
 
120
- // Prevent reordering if there's no data or data is still loading
121
104
  if (!todosLosRegistros || todosLosRegistros.length === 0 || loading) {
122
105
  resetDragState();
123
106
  return;
@@ -125,17 +108,12 @@
125
108
 
126
109
  event.preventDefault();
127
110
 
128
- // Create a new array with reordered items
129
111
  const newItems = [...todosLosRegistros];
130
112
  const draggedItem = newItems[draggedIndex];
131
113
 
132
- // Remove the dragged item from its original position
133
114
  newItems.splice(draggedIndex, 1);
134
-
135
- // Insert the dragged item at the new position
136
115
  newItems.splice(dropIndex, 0, draggedItem);
137
116
 
138
- // Update the order values and track changes
139
117
  const changes: any[] = [];
140
118
 
141
119
  newItems.forEach((item, index) => {
@@ -149,15 +127,11 @@
149
127
  });
150
128
  }
151
129
 
152
- // Update the item's order in the array
153
130
  item[orderField] = newOrder;
154
131
  });
155
132
 
156
- // Update the items array
157
133
  todosLosRegistros = newItems;
158
- reorderedItems = changes;
159
134
 
160
- // Emit the reorder event
161
135
  dispatch("reorderChange", { reorderedItems: changes });
162
136
 
163
137
  resetDragState();
@@ -177,713 +151,43 @@
177
151
  <div class="table-container">
178
152
  <div class="table-scroll">
179
153
  <table class="data-table" bind:this={tablaExport}>
180
- <thead class="table-header">
181
- <tr>
182
- {#if dragEnabled}
183
- <th
184
- class="table-header-cell drag-header non-sortable header-sticky-intersection"
185
- >
186
- <div class="drag-handle-header">
187
- <svg
188
- xmlns="http://www.w3.org/2000/svg"
189
- width="16"
190
- height="16"
191
- viewBox="0 0 24 24"
192
- fill="none"
193
- stroke="currentColor"
194
- stroke-width="2"
195
- stroke-linecap="round"
196
- stroke-linejoin="round"
197
- >
198
- <circle cx="9" cy="12" r="1"></circle>
199
- <circle cx="9" cy="5" r="1"></circle>
200
- <circle cx="9" cy="19" r="1"></circle>
201
- <circle cx="15" cy="12" r="1"></circle>
202
- <circle cx="15" cy="5" r="1"></circle>
203
- <circle cx="15" cy="19" r="1"></circle>
204
- </svg>
205
- </div>
206
- </th>
207
- {/if}
208
- {#if expandEnabled}
209
- <th
210
- class="table-header-cell expand-header non-sortable {!dragEnabled
211
- ? 'header-sticky-intersection borderleft'
212
- : ''}"
213
- >
214
- </th>
215
- {/if}
216
- {#each tableHeaders as tableHeader, index}
217
- {#if tableHeader.biSort == false}
218
- <th
219
- class="table-header-cell {index == 0 &&
220
- !dragEnabled &&
221
- !expandEnabled
222
- ? 'borderleft header-sticky-intersection'
223
- : ''} non-sortable"
224
- style="text-align: {tableHeader.align ??
225
- 'center'}"
226
- >
227
- {tableHeader.titulo}
228
- </th>
229
- {:else}
230
- <th
231
- on:click={() => dispatchSort(tableHeader.campo)}
232
- class="table-header-cell {index == 0 &&
233
- !dragEnabled &&
234
- !expandEnabled
235
- ? 'borderleft header-sticky-intersection'
236
- : ''} sortable"
237
- style="text-align: {tableHeader.align ??
238
- 'left'}"
239
- >
240
- <h1>{tableHeader.titulo}</h1>
241
- {#if selectedSort == tableHeader.campo}
242
- {#if selectedAscOrDesc == "asc"}
243
- <div class="sort-icon">
244
- <svg
245
- xmlns="http://www.w3.org/2000/svg"
246
- width="16"
247
- height="16"
248
- viewBox="0 0 24 24"
249
- fill="none"
250
- stroke="currentColor"
251
- stroke-width="2"
252
- stroke-linecap="round"
253
- stroke-linejoin="round"
254
- class="sort-arrow"
255
- >
256
- <polyline
257
- points="6 9 12 15 18 9"
258
- ></polyline>
259
- </svg>
260
- </div>
261
- {:else}
262
- <div class="sort-icon">
263
- <svg
264
- xmlns="http://www.w3.org/2000/svg"
265
- width="16"
266
- height="16"
267
- viewBox="0 0 24 24"
268
- fill="none"
269
- stroke="currentColor"
270
- stroke-width="2"
271
- stroke-linecap="round"
272
- stroke-linejoin="round"
273
- class="sort-arrow"
274
- >
275
- <polyline
276
- points="18 15 12 9 6 15"
277
- ></polyline>
278
- </svg>
279
- </div>
280
- {/if}
281
- {/if}
282
- </th>
283
- {/if}
284
- {/each}
285
- </tr>
286
- </thead>
154
+ <TableHeaderComponent
155
+ {tableHeaders}
156
+ {dragEnabled}
157
+ {expandEnabled}
158
+ selectedSort={$selectedSort}
159
+ selectedAscOrDesc={$selectedAscOrDesc}
160
+ onSort={handleSort}
161
+ />
287
162
 
288
163
  {#if todosLosRegistros && !loading}
289
164
  <tbody>
290
165
  {#each todosLosRegistros as item, index}
291
- <tr
292
- class="table-row {selectedRowId === index
293
- ? 'selected'
294
- : ''} {dragEnabled &&
295
- !loading &&
296
- todosLosRegistros.length > 0
297
- ? 'draggable-row'
298
- : ''} {draggedIndex === index
299
- ? 'dragging'
300
- : ''} {dragOverIndex === index
301
- ? 'drag-over'
302
- : ''}"
303
- draggable={dragEnabled &&
304
- !loading &&
305
- todosLosRegistros.length > 0}
306
- on:click={() => handleRowClick(index)}
307
- on:dragstart={(e) => handleDragStart(e, index)}
308
- on:dragover={(e) => handleDragOver(e, index)}
309
- on:dragleave={handleDragLeave}
310
- on:drop={(e) => handleDrop(e, index)}
311
- on:dragend={handleDragEnd}
312
- >
313
- {#if dragEnabled}
314
- <td
315
- class="table-cell drag-handle-cell sticky-cell"
316
- >
317
- <div
318
- class="drag-handle"
319
- title="Drag to reorder"
320
- >
321
- <svg
322
- xmlns="http://www.w3.org/2000/svg"
323
- width="16"
324
- height="16"
325
- viewBox="0 0 24 24"
326
- fill="none"
327
- stroke="currentColor"
328
- stroke-width="2"
329
- stroke-linecap="round"
330
- stroke-linejoin="round"
331
- >
332
- <circle cx="9" cy="12" r="1"
333
- ></circle>
334
- <circle cx="9" cy="5" r="1"
335
- ></circle>
336
- <circle cx="9" cy="19" r="1"
337
- ></circle>
338
- <circle cx="15" cy="12" r="1"
339
- ></circle>
340
- <circle cx="15" cy="5" r="1"
341
- ></circle>
342
- <circle cx="15" cy="19" r="1"
343
- ></circle>
344
- </svg>
345
- </div>
346
- </td>
347
- {/if}
348
- {#if expandEnabled}
349
- <td
350
- class="table-cell expand-cell {!dragEnabled
351
- ? 'sticky-cell'
352
- : ''}"
353
- >
354
- {#if item[subRowsField] && item[subRowsField].length > 0}
355
- <button
356
- type="button"
357
- class="expand-button"
358
- on:click|stopPropagation={() =>
359
- toggleExpand(item[idField])}
360
- >
361
- <svg
362
- class="chevron-icon {expandedRows.has(
363
- item[idField]
364
- )
365
- ? 'expanded'
366
- : ''}"
367
- xmlns="http://www.w3.org/2000/svg"
368
- width="24"
369
- height="24"
370
- viewBox="0 0 24 24"
371
- fill="none"
372
- stroke="currentColor"
373
- stroke-width="2"
374
- stroke-linecap="round"
375
- stroke-linejoin="round"
376
- >
377
- <polyline
378
- points="9 18 15 12 9 6"
379
- ></polyline>
380
- </svg>
381
- </button>
382
- {/if}
383
- </td>
384
- {/if}
385
- {#each tableHeaders as tableBodyItem, i}
386
- {#if tableBodyItem.tipo == "Text"}
387
- <td
388
- class="table-cell {i == 0 &&
389
- !dragEnabled &&
390
- !expandEnabled
391
- ? 'sticky-cell'
392
- : ''}"
393
- >
394
- <p
395
- class="cell-content {item[
396
- tableBodyItem.colorCampo ?? ''
397
- ] ?? ''} {tableBodyItem.biBold
398
- ? 'bold'
399
- : ''}"
400
- style="text-align: {tableBodyItem.align ??
401
- 'left'}"
402
- >
403
- {item[tableBodyItem.campo] ?? ""}
404
- </p>
405
- </td>
406
- {:else if tableBodyItem.tipo == "Number"}
407
- <td
408
- class="table-cell {i == 0 &&
409
- !dragEnabled &&
410
- !expandEnabled
411
- ? 'sticky-cell'
412
- : ''}"
413
- >
414
- <p
415
- class="cell-content {item[
416
- tableBodyItem.colorCampo ?? ''
417
- ] ?? ''} {tableBodyItem.biBold
418
- ? 'bold'
419
- : ''}"
420
- style="text-align: {tableBodyItem.align ??
421
- 'left'}"
422
- >
423
- {item[tableBodyItem.campo] ?? ""}
424
- </p>
425
- </td>
426
- {:else if tableBodyItem.tipo == "Datetime"}
427
- <td
428
- class="table-cell {i == 0 &&
429
- !dragEnabled &&
430
- !expandEnabled
431
- ? 'sticky-cell'
432
- : ''}"
433
- >
434
- <p
435
- class="cell-content {item[
436
- tableBodyItem.colorCampo ?? ''
437
- ] ?? ''} {tableBodyItem.biBold
438
- ? 'bold'
439
- : ''}"
440
- style="text-align: {tableBodyItem.align ??
441
- 'left'}"
442
- >
443
- {item[tableBodyItem.campo]?.replace(
444
- "T",
445
- ":"
446
- ) ?? ":"}
447
- </p>
448
- </td>
449
- {:else if tableBodyItem.tipo == "Date"}
450
- <td
451
- class="table-cell {i == 0 &&
452
- !dragEnabled &&
453
- !expandEnabled
454
- ? 'sticky-cell'
455
- : ''}"
456
- >
457
- <p
458
- class="cell-content {item[
459
- tableBodyItem.colorCampo ?? ''
460
- ] ?? ''} {tableBodyItem.biBold
461
- ? 'bold'
462
- : ''}"
463
- style="text-align: {tableBodyItem.align ??
464
- 'left'}"
465
- >
466
- {item[tableBodyItem.campo]?.split(
467
- "T"
468
- )[0] ?? ":"}
469
- </p>
470
- </td>
471
- {:else if tableBodyItem.tipo == "Bool"}
472
- <td
473
- class="table-cell {i == 0 &&
474
- !dragEnabled &&
475
- !expandEnabled
476
- ? 'sticky-cell'
477
- : ''}"
478
- >
479
- {#if item[tableBodyItem.campo] === true}
480
- <p
481
- class="cell-content {item[
482
- tableBodyItem.colorCampo ??
483
- ''
484
- ] ?? ''} {tableBodyItem.biBold
485
- ? 'bold'
486
- : ''}"
487
- style="text-align: {tableBodyItem.align ??
488
- 'left'}"
489
- >
490
- <i class="fas fa-check"></i>
491
- </p>
492
- {:else}
493
- <p
494
- class="cell-content {item[
495
- tableBodyItem.colorCampo ??
496
- ''
497
- ] ?? ''} {tableBodyItem.biBold
498
- ? 'bold'
499
- : ''}"
500
- style="text-align: {tableBodyItem.align ??
501
- 'left'}"
502
- >
503
- <i class="fas fa-minus"></i>
504
- </p>
505
- {/if}
506
- </td>
507
- {:else if tableBodyItem.tipo == "EditableBool"}
508
- <td
509
- class="table-cell {i == 0 &&
510
- !dragEnabled &&
511
- !expandEnabled
512
- ? 'sticky-cell'
513
- : ''}"
514
- style="text-align: {tableBodyItem.align ??
515
- 'center'}"
516
- >
517
- {#if item[tableBodyItem.campo]}
518
- <button
519
- class="editable-checkbox {item[
520
- tableBodyItem.campo
521
- ]
522
- ? 'checked'
523
- : ''}"
524
- on:click={async () => {
525
- const newValue =
526
- !item[
527
- tableBodyItem.campo
528
- ];
529
- item[tableBodyItem.campo] =
530
- newValue;
531
- if (
532
- tableBodyItem.onUpdate
533
- ) {
534
- await tableBodyItem.onUpdate(
535
- item[idField],
536
- tableBodyItem.campo,
537
- newValue
538
- );
539
- }
540
- }}
541
- >
542
- <i class="fas fa-check"></i>
543
- </button>
544
- {:else}
545
- <button
546
- class="editable-checkbox {item[
547
- tableBodyItem.campo
548
- ]
549
- ? 'checked'
550
- : ''}"
551
- on:click={async () => {
552
- const newValue =
553
- !item[
554
- tableBodyItem.campo
555
- ];
556
- item[tableBodyItem.campo] =
557
- newValue;
558
- if (
559
- tableBodyItem.onUpdate
560
- ) {
561
- await tableBodyItem.onUpdate(
562
- item[idField],
563
- tableBodyItem.campo,
564
- newValue
565
- );
566
- }
567
- }}
568
- >
569
- <i class="fas fa-minus"></i>
570
- </button>
571
- {/if}
572
- </td>
573
- {:else if tableBodyItem.tipo == "EditableText"}
574
- <td
575
- class="table-cell {i == 0 &&
576
- !dragEnabled &&
577
- !expandEnabled
578
- ? 'sticky-cell'
579
- : ''}"
580
- >
581
- <input
582
- type="text"
583
- class="editable-input"
584
- bind:value={
585
- item[tableBodyItem.campo]
586
- }
587
- on:blur={async (e) => {
588
- if (tableBodyItem.onUpdate) {
589
- await tableBodyItem.onUpdate(
590
- item[idField],
591
- tableBodyItem.campo,
592
- e.currentTarget.value
593
- );
594
- }
595
- }}
596
- on:keydown={async (e) => {
597
- if (e.key === "Enter") {
598
- e.currentTarget.blur();
599
- }
600
- }}
601
- style="text-align: {tableBodyItem.align ??
602
- 'left'}"
603
- />
604
- </td>
605
- {:else if tableBodyItem.tipo == "EditableNumber"}
606
- <td
607
- class="table-cell {i == 0 &&
608
- !dragEnabled &&
609
- !expandEnabled
610
- ? 'sticky-cell'
611
- : ''}"
612
- >
613
- <input
614
- type="number"
615
- class="editable-input"
616
- bind:value={
617
- item[tableBodyItem.campo]
618
- }
619
- on:blur={async (e) => {
620
- if (tableBodyItem.onUpdate) {
621
- const numValue = parseFloat(
622
- e.currentTarget.value
623
- );
624
- await tableBodyItem.onUpdate(
625
- item[idField],
626
- tableBodyItem.campo,
627
- isNaN(numValue)
628
- ? null
629
- : numValue
630
- );
631
- }
632
- }}
633
- on:keydown={async (e) => {
634
- if (e.key === "Enter") {
635
- e.currentTarget.blur();
636
- }
637
- }}
638
- style="text-align: {tableBodyItem.align ??
639
- 'right'}"
640
- />
641
- </td>
642
- {:else if tableBodyItem.tipo == "Image"}
643
- <td
644
- class="table-cell {i == 0 &&
645
- !dragEnabled &&
646
- !expandEnabled
647
- ? 'sticky-cell'
648
- : ''}"
649
- >
650
- <img
651
- class="crud-image cursor-pointer"
652
- src={item[tableBodyItem.campo] ??
653
- ""}
654
- alt="image"
655
- on:click={() =>
656
- openImageModal(
657
- item[tableBodyItem.campo]
658
- )}
659
- />
660
- </td>
661
- {:else if tableBodyItem.tipo == "Buttons"}
662
- <CrudTableButtons
663
- id={item[tableBodyItem.campo]}
664
- buttonsConfig={tableBodyItem.buttonsConfig ??
665
- []}
666
- align={tableBodyItem.align ?? "center"}
667
- />
668
- {:else if tableBodyItem.tipo == "DynamicButton"}
669
- <td
670
- class="table-cell {i == 0 &&
671
- !dragEnabled &&
672
- !expandEnabled
673
- ? 'sticky-cell'
674
- : ''}"
675
- style="text-align: {tableBodyItem.align ??
676
- 'center'}"
677
- >
678
- <button
679
- type="button"
680
- class="dynamic-button {item[
681
- tableBodyItem.colorField ?? ''
682
- ] ?? ''}"
683
- on:click={() => {
684
- if (
685
- tableBodyItem.onButtonClick
686
- ) {
687
- tableBodyItem.onButtonClick(
688
- item[idField],
689
- item
690
- );
691
- }
692
- }}
693
- >
694
- {#if tableBodyItem.iconField && item[tableBodyItem.iconField]}
695
- {#if !tableBodyItem.iconPosition || tableBodyItem.iconPosition === "left"}
696
- <i
697
- class="{item[
698
- tableBodyItem
699
- .iconField
700
- ]} dynamic-button-icon-left"
701
- ></i>
702
- {/if}
703
- {/if}
704
- {#if tableBodyItem.textField && item[tableBodyItem.textField]}
705
- <span
706
- >{item[
707
- tableBodyItem.textField
708
- ]}</span
709
- >
710
- {/if}
711
- {#if tableBodyItem.iconField && item[tableBodyItem.iconField]}
712
- {#if tableBodyItem.iconPosition === "right"}
713
- <i
714
- class="{item[
715
- tableBodyItem
716
- .iconField
717
- ]} dynamic-button-icon-right"
718
- ></i>
719
- {/if}
720
- {/if}
721
- </button>
722
- </td>
723
- {:else if tableBodyItem.tipo == "ImageButton"}
724
- <td
725
- class="table-cell {i == 0 &&
726
- !dragEnabled &&
727
- !expandEnabled
728
- ? 'sticky-cell'
729
- : ''}"
730
- style="text-align: {tableBodyItem.align ??
731
- 'center'}"
732
- >
733
- <button
734
- type="button"
735
- class="image-button-wrapper"
736
- on:click={() => {
737
- if (tableBodyItem.action) {
738
- tableBodyItem.action(
739
- item[idField]
740
- );
741
- }
742
- }}
743
- >
744
- <img
745
- src={item[
746
- tableBodyItem.imageField ??
747
- ""
748
- ] ?? ""}
749
- alt="button"
750
- class="image-button image-button-{tableBodyItem.imageSize ??
751
- 'md'}"
752
- />
753
- </button>
754
- </td>
755
- {:else if tableBodyItem.tipo == "DualTextButton"}
756
- <td
757
- class="table-cell {i == 0 &&
758
- !dragEnabled &&
759
- !expandEnabled
760
- ? 'sticky-cell'
761
- : ''}"
762
- style="text-align: {tableBodyItem.align ??
763
- 'center'}"
764
- >
765
- <button
766
- type="button"
767
- class="dual-text-button"
768
- on:click={() => {
769
- if (
770
- tableBodyItem.onButtonClick
771
- ) {
772
- tableBodyItem.onButtonClick(
773
- item[idField],
774
- item
775
- );
776
- }
777
- }}
778
- >
779
- {#if tableBodyItem.textField1 && item[tableBodyItem.textField1]}
780
- <div
781
- class="dual-text-1 {item[
782
- tableBodyItem.colorField1 ??
783
- ''
784
- ] ?? ''}"
785
- >
786
- {item[
787
- tableBodyItem.textField1
788
- ]}
789
- </div>
790
- {/if}
791
- {#if tableBodyItem.textField2 && item[tableBodyItem.textField2]}
792
- <div
793
- class="dual-text-2 {item[
794
- tableBodyItem.colorField2 ??
795
- ''
796
- ] ?? ''}"
797
- >
798
- {item[
799
- tableBodyItem.textField2
800
- ]}
801
- </div>
802
- {/if}
803
- </button>
804
- </td>
805
- {:else if tableBodyItem.tipo == "ConditionalCell"}
806
- {@const condition = tableBodyItem.conditionField ? item[tableBodyItem.conditionField] : false}
807
- {@const config = condition ? tableBodyItem.whenTrue : tableBodyItem.whenFalse}
808
- <td
809
- class="table-cell {i == 0 &&
810
- !dragEnabled &&
811
- !expandEnabled
812
- ? 'sticky-cell'
813
- : ''}"
814
- style="text-align: {tableBodyItem.align ??
815
- 'center'}"
816
- >
817
- {#if config?.tipo === "Text"}
818
- <p
819
- class="cell-content {config.colorField ? (item[config.colorField] ?? '') : ''} {tableBodyItem.biBold ? 'bold' : ''}"
820
- style="text-align: {tableBodyItem.align ?? 'left'}"
821
- >
822
- {config.textField ? (item[config.textField] ?? '') : ''}
823
- </p>
824
- {:else if config?.tipo === "DualTextButton"}
825
- <button
826
- type="button"
827
- class="dual-text-button"
828
- on:click={() => {
829
- if (tableBodyItem.onButtonClick) {
830
- tableBodyItem.onButtonClick(
831
- item[idField],
832
- item
833
- );
834
- }
835
- }}
836
- >
837
- {#if config.textField1 && item[config.textField1]}
838
- <div class="dual-text-1 {config.colorField1 ? (item[config.colorField1] ?? '') : ''}">
839
- {item[config.textField1]}
840
- </div>
841
- {/if}
842
- {#if config.textField2 && item[config.textField2]}
843
- <div class="dual-text-2 {config.colorField2 ? (item[config.colorField2] ?? '') : ''}">
844
- {item[config.textField2]}
845
- </div>
846
- {/if}
847
- </button>
848
- {/if}
849
- </td>
850
- {:else if tableBodyItem.tipo == "MultiTextButton"}
851
- {@const items = tableBodyItem.itemsField ? (item[tableBodyItem.itemsField] ?? []) : []}
852
- {@const layout = tableBodyItem.multiLayout ?? 'horizontal'}
853
- <td
854
- class="table-cell {i == 0 &&
855
- !dragEnabled &&
856
- !expandEnabled
857
- ? 'sticky-cell'
858
- : ''}"
859
- style="text-align: {tableBodyItem.align ??
860
- 'center'}"
861
- >
862
- <button
863
- type="button"
864
- class="multi-text-button multi-text-{layout}"
865
- on:click={() => {
866
- if (tableBodyItem.onButtonClick) {
867
- tableBodyItem.onButtonClick(
868
- item[idField],
869
- item
870
- );
871
- }
872
- }}
873
- >
874
- {#each items as multiItem, idx}
875
- <div class="multi-text-item {multiItem.color ?? ''}">
876
- {multiItem.text ?? ''}
877
- </div>
878
- {/each}
879
- </button>
880
- </td>
881
- {/if}
882
- {/each}
883
- </tr>
166
+ <TableRow
167
+ {item}
168
+ {index}
169
+ {tableHeaders}
170
+ {idField}
171
+ {dragEnabled}
172
+ {expandEnabled}
173
+ {subRowsField}
174
+ {selectedRowId}
175
+ {draggedIndex}
176
+ {dragOverIndex}
177
+ {loading}
178
+ expandedRows={$expandedRows}
179
+ onRowClick={handleRowClick}
180
+ onDragStart={handleDragStart}
181
+ onDragOver={handleDragOver}
182
+ onDragLeave={handleDragLeave}
183
+ onDrop={handleDrop}
184
+ onDragEnd={handleDragEnd}
185
+ onToggleExpand={toggleExpand}
186
+ onImageClick={openImageModal}
187
+ />
884
188
 
885
189
  <!-- Subrows as nested table -->
886
- {#if expandEnabled && expandedRows.has(item[idField]) && item[subRowsField] && item[subRowsField].length > 0}
190
+ {#if expandEnabled && $expandedRows.has(item[idField]) && item[subRowsField] && item[subRowsField].length > 0}
887
191
  <tr class="sub-row-container">
888
192
  <td
889
193
  colspan={tableHeaders.length +
@@ -891,386 +195,12 @@
891
195
  (expandEnabled ? 1 : 0)}
892
196
  class="sub-row-cell"
893
197
  >
894
- <table class="sub-table">
895
- <tbody>
896
- {#each item[subRowsField] as subItem, subIndex}
897
- <tr class="sub-row">
898
- {#each effectiveSubRowHeaders as subHeader, i}
899
- {#if subHeader.tipo == "Text"}
900
- <td
901
- class="table-cell"
902
- >
903
- <p
904
- class="cell-content {subItem[
905
- subHeader.colorCampo ??
906
- ''
907
- ] ??
908
- ''} {subHeader.biBold
909
- ? 'bold'
910
- : ''}"
911
- style="text-align: {subHeader.align ??
912
- 'left'}"
913
- >
914
- {subItem[
915
- subHeader
916
- .campo
917
- ] ?? ""}
918
- </p>
919
- </td>
920
- {:else if subHeader.tipo == "Number"}
921
- <td
922
- class="table-cell"
923
- >
924
- <p
925
- class="cell-content {subItem[
926
- subHeader.colorCampo ??
927
- ''
928
- ] ??
929
- ''} {subHeader.biBold
930
- ? 'bold'
931
- : ''}"
932
- style="text-align: {subHeader.align ??
933
- 'left'}"
934
- >
935
- {subItem[
936
- subHeader
937
- .campo
938
- ] ?? ""}
939
- </p>
940
- </td>
941
- {:else if subHeader.tipo == "Date"}
942
- <td
943
- class="table-cell"
944
- >
945
- <p
946
- class="cell-content {subItem[
947
- subHeader.colorCampo ??
948
- ''
949
- ] ??
950
- ''} {subHeader.biBold
951
- ? 'bold'
952
- : ''}"
953
- style="text-align: {subHeader.align ??
954
- 'left'}"
955
- >
956
- {subItem[
957
- subHeader
958
- .campo
959
- ]?.split(
960
- "T"
961
- )[0] ?? ":"}
962
- </p>
963
- </td>
964
- {:else if subHeader.tipo == "Datetime"}
965
- <td
966
- class="table-cell"
967
- >
968
- <p
969
- class="cell-content {subItem[
970
- subHeader.colorCampo ??
971
- ''
972
- ] ??
973
- ''} {subHeader.biBold
974
- ? 'bold'
975
- : ''}"
976
- style="text-align: {subHeader.align ??
977
- 'left'}"
978
- >
979
- {subItem[
980
- subHeader
981
- .campo
982
- ]?.replace(
983
- "T",
984
- ":"
985
- ) ?? ":"}
986
- </p>
987
- </td>
988
- {:else if subHeader.tipo == "Bool"}
989
- <td
990
- class="table-cell"
991
- >
992
- {#if subItem[subHeader.campo] === true}
993
- <p
994
- class="cell-content {subItem[
995
- subHeader.colorCampo ??
996
- ''
997
- ] ??
998
- ''} {subHeader.biBold
999
- ? 'bold'
1000
- : ''}"
1001
- style="text-align: {subHeader.align ??
1002
- 'left'}"
1003
- >
1004
- <i
1005
- class="fas fa-check"
1006
- ></i>
1007
- </p>
1008
- {:else}
1009
- <p
1010
- class="cell-content {subItem[
1011
- subHeader.colorCampo ??
1012
- ''
1013
- ] ??
1014
- ''} {subHeader.biBold
1015
- ? 'bold'
1016
- : ''}"
1017
- style="text-align: {subHeader.align ??
1018
- 'left'}"
1019
- >
1020
- <i
1021
- class="fas fa-minus"
1022
- ></i>
1023
- </p>
1024
- {/if}
1025
- </td>
1026
- {:else if subHeader.tipo == "Image"}
1027
- <td
1028
- class="table-cell"
1029
- >
1030
- <img
1031
- class="crud-image cursor-pointer"
1032
- src={subItem[
1033
- subHeader
1034
- .campo
1035
- ] ?? ""}
1036
- alt="image"
1037
- on:click={() =>
1038
- openImageModal(
1039
- subItem[
1040
- subHeader
1041
- .campo
1042
- ]
1043
- )}
1044
- />
1045
- </td>
1046
- {:else if subHeader.tipo == "Buttons"}
1047
- <CrudTableButtons
1048
- id={subItem[
1049
- subHeader
1050
- .campo
1051
- ]}
1052
- buttonsConfig={subHeader.buttonsConfig ??
1053
- []}
1054
- align={subHeader.align ??
1055
- "center"}
1056
- />
1057
- {:else if subHeader.tipo == "DynamicButton"}
1058
- <td
1059
- class="table-cell"
1060
- style="text-align: {subHeader.align ??
1061
- 'center'}"
1062
- >
1063
- <button
1064
- type="button"
1065
- class="dynamic-button {subItem[
1066
- subHeader.colorField ??
1067
- ''
1068
- ] ?? ''}"
1069
- on:click={() => {
1070
- if (
1071
- subHeader.onButtonClick
1072
- ) {
1073
- subHeader.onButtonClick(
1074
- subItem[
1075
- idField
1076
- ],
1077
- subItem
1078
- );
1079
- }
1080
- }}
1081
- >
1082
- {#if subHeader.iconField && subItem[subHeader.iconField]}
1083
- {#if !subHeader.iconPosition || subHeader.iconPosition === "left"}
1084
- <i
1085
- class="{subItem[
1086
- subHeader
1087
- .iconField
1088
- ]} dynamic-button-icon-left"
1089
-
1090
- ></i>
1091
- {/if}
1092
- {/if}
1093
- {#if subHeader.textField && subItem[subHeader.textField]}
1094
- <span
1095
- >{subItem[
1096
- subHeader
1097
- .textField
1098
- ]}</span
1099
- >
1100
- {/if}
1101
- {#if subHeader.iconField && subItem[subHeader.iconField]}
1102
- {#if subHeader.iconPosition === "right"}
1103
- <i
1104
- class="{subItem[
1105
- subHeader
1106
- .iconField
1107
- ]} dynamic-button-icon-right"
1108
-
1109
- ></i>
1110
- {/if}
1111
- {/if}
1112
- </button>
1113
- </td>
1114
- {:else if subHeader.tipo == "ImageButton"}
1115
- <td
1116
- class="table-cell"
1117
- style="text-align: {subHeader.align ??
1118
- 'center'}"
1119
- >
1120
- <button
1121
- type="button"
1122
- class="image-button-wrapper"
1123
- on:click={() => {
1124
- if (
1125
- subHeader.action
1126
- ) {
1127
- subHeader.action(
1128
- subItem[
1129
- idField
1130
- ]
1131
- );
1132
- }
1133
- }}
1134
- >
1135
- <img
1136
- src={subItem[
1137
- subHeader.imageField ??
1138
- ""
1139
- ] ?? ""}
1140
- alt="button"
1141
- class="image-button image-button-{subHeader.imageSize ??
1142
- 'md'}"
1143
- />
1144
- </button>
1145
- </td>
1146
- {:else if subHeader.tipo == "DualTextButton"}
1147
- <td
1148
- class="table-cell"
1149
- style="text-align: {subHeader.align ??
1150
- 'center'}"
1151
- >
1152
- <button
1153
- type="button"
1154
- class="dual-text-button"
1155
- on:click={() => {
1156
- if (
1157
- subHeader.onButtonClick
1158
- ) {
1159
- subHeader.onButtonClick(
1160
- subItem[
1161
- idField
1162
- ],
1163
- subItem
1164
- );
1165
- }
1166
- }}
1167
- >
1168
- {#if subHeader.textField1 && subItem[subHeader.textField1]}
1169
- <span
1170
- class="dual-text-1 {subItem[
1171
- subHeader.colorField1 ??
1172
- ''
1173
- ] ??
1174
- ''}"
1175
- >
1176
- {subItem[
1177
- subHeader
1178
- .textField1
1179
- ]}
1180
- </span>
1181
- {/if}
1182
- {#if subHeader.textField2 && subItem[subHeader.textField2]}
1183
- <span
1184
- class="dual-text-2 {subItem[
1185
- subHeader.colorField2 ??
1186
- ''
1187
- ] ??
1188
- ''}"
1189
- >
1190
- {subItem[
1191
- subHeader
1192
- .textField2
1193
- ]}
1194
- </span>
1195
- {/if}
1196
- </button>
1197
- </td>
1198
- {:else if subHeader.tipo == "ConditionalCell"}
1199
- {@const condition = subHeader.conditionField ? subItem[subHeader.conditionField] : false}
1200
- {@const config = condition ? subHeader.whenTrue : subHeader.whenFalse}
1201
- <td
1202
- class="table-cell"
1203
- style="text-align: {subHeader.align ?? 'center'}"
1204
- >
1205
- {#if config?.tipo === "Text"}
1206
- <p
1207
- class="cell-content {config.colorField ? (subItem[config.colorField] ?? '') : ''} {subHeader.biBold ? 'bold' : ''}"
1208
- style="text-align: {subHeader.align ?? 'left'}"
1209
- >
1210
- {config.textField ? (subItem[config.textField] ?? '') : ''}
1211
- </p>
1212
- {:else if config?.tipo === "DualTextButton"}
1213
- <button
1214
- type="button"
1215
- class="dual-text-button"
1216
- on:click={() => {
1217
- if (subHeader.onButtonClick) {
1218
- subHeader.onButtonClick(
1219
- subItem[idField],
1220
- subItem
1221
- );
1222
- }
1223
- }}
1224
- >
1225
- {#if config.textField1 && subItem[config.textField1]}
1226
- <span class="dual-text-1 {config.colorField1 ? (subItem[config.colorField1] ?? '') : ''}">
1227
- {subItem[config.textField1]}
1228
- </span>
1229
- {/if}
1230
- {#if config.textField2 && subItem[config.textField2]}
1231
- <span class="dual-text-2 {config.colorField2 ? (subItem[config.colorField2] ?? '') : ''}">
1232
- {subItem[config.textField2]}
1233
- </span>
1234
- {/if}
1235
- </button>
1236
- {/if}
1237
- </td>
1238
- {:else if subHeader.tipo == "MultiTextButton"}
1239
- {@const items = subHeader.itemsField ? (subItem[subHeader.itemsField] ?? []) : []}
1240
- {@const layout = subHeader.multiLayout ?? 'horizontal'}
1241
- <td
1242
- class="table-cell"
1243
- style="text-align: {subHeader.align ?? 'center'}"
1244
- >
1245
- <button
1246
- type="button"
1247
- class="multi-text-button multi-text-{layout}"
1248
- on:click={() => {
1249
- if (subHeader.onButtonClick) {
1250
- subHeader.onButtonClick(
1251
- subItem[idField],
1252
- subItem
1253
- );
1254
- }
1255
- }}
1256
- >
1257
- {#each items as multiItem, idx}
1258
- <div class="multi-text-item {multiItem.color ?? ''}">
1259
- {multiItem.text ?? ''}
1260
- </div>
1261
- {/each}
1262
- </button>
1263
- </td>
1264
- {:else}
1265
- <td
1266
- class="table-cell"
1267
- ></td>
1268
- {/if}
1269
- {/each}
1270
- </tr>
1271
- {/each}
1272
- </tbody>
1273
- </table>
198
+ <SubRowsTable
199
+ subRows={item[subRowsField]}
200
+ subRowHeaders={effectiveSubRowHeaders}
201
+ {idField}
202
+ onImageClick={openImageModal}
203
+ />
1274
204
  </td>
1275
205
  </tr>
1276
206
  {/if}
@@ -1314,667 +244,3 @@
1314
244
  {/if}
1315
245
  </div>
1316
246
  </div>
1317
-
1318
- <style>
1319
- .table-container {
1320
- position: relative;
1321
- display: flex;
1322
- flex-direction: column;
1323
- min-width: 0;
1324
- word-break: break-word;
1325
- z-index: 30;
1326
- width: 100%;
1327
- margin-bottom: 0.75rem;
1328
- margin-top: 0.75rem;
1329
- }
1330
-
1331
- .table-scroll {
1332
- overflow: auto;
1333
- max-height: 80vh;
1334
- }
1335
-
1336
- /* Scrollbar Styles for WebKit browsers (Chrome, Safari, Edge) */
1337
- .table-scroll::-webkit-scrollbar {
1338
- width: var(--grav-crud-scrollbar-width);
1339
- height: var(--grav-crud-scrollbar-width);
1340
- }
1341
-
1342
- .table-scroll::-webkit-scrollbar-track {
1343
- background: var(--grav-crud-scrollbar-track);
1344
- border-radius: var(--grav-border-radius-sm);
1345
- }
1346
-
1347
- .table-scroll::-webkit-scrollbar-thumb {
1348
- background: var(--grav-crud-scrollbar-thumb);
1349
- border-radius: var(--grav-border-radius-sm);
1350
- transition: background-color 0.2s ease;
1351
- }
1352
-
1353
- .table-scroll::-webkit-scrollbar-thumb:hover {
1354
- background: var(--grav-crud-scrollbar-thumb-hover);
1355
- }
1356
-
1357
- /* Scrollbar Styles for Firefox */
1358
- .table-scroll {
1359
- scrollbar-width: thin;
1360
- scrollbar-color: var(--grav-crud-scrollbar-thumb)
1361
- var(--grav-crud-scrollbar-track);
1362
- }
1363
-
1364
- .data-table {
1365
- width: 100%;
1366
- border-collapse: collapse;
1367
- table-layout: auto;
1368
- }
1369
-
1370
- .table-header {
1371
- position: sticky;
1372
- top: 0;
1373
- z-index: 20;
1374
- border-bottom-style: solid;
1375
- border-bottom-color: var(--grav-crud-color-border);
1376
- border-bottom-width: var(--grav-crud-cell-border-width, 1.5px);
1377
- background-color: rgba(255, 255, 255, 0.8);
1378
- backdrop-filter: blur(8px);
1379
- -webkit-backdrop-filter: blur(8px);
1380
- box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
1381
- }
1382
-
1383
- @supports (backdrop-filter: blur(8px)) or
1384
- (-webkit-backdrop-filter: blur(8px)) {
1385
- .table-header {
1386
- background-color: var(
1387
- --grav-crud-color-bg,
1388
- rgba(255, 255, 255, 0.85)
1389
- );
1390
- }
1391
- }
1392
-
1393
- .table-header-cell {
1394
- padding: 0.75rem;
1395
- text-align: center;
1396
- padding-top: 0.25rem;
1397
- padding-bottom: 0.25rem;
1398
- font-family: var(--grav-crud-header-font-family, "mundial", sans-serif);
1399
- font-size: var(--grav-crud-header-font-size, 0.75rem);
1400
- font-weight: var(--grav-crud-header-font-weight, 400);
1401
- line-height: var(--grav-crud-header-line-height, 1.5);
1402
- text-transform: uppercase;
1403
- white-space: nowrap;
1404
- background-color: inherit;
1405
- color: var(--grav-crud-color-neutral);
1406
- border-left: 0;
1407
- }
1408
-
1409
- .non-sortable {
1410
- cursor: not-allowed;
1411
- }
1412
-
1413
- .sortable {
1414
- cursor: pointer;
1415
- text-align: left;
1416
- }
1417
-
1418
- .borderleft {
1419
- border-left-width: var(--grav-crud-table-border-width, 1.5px);
1420
- border-left-style: solid;
1421
- border-left-color: var(--grav-crud-color-border);
1422
- }
1423
-
1424
- /* Special class for header cells that are sticky on both axes (top + left) */
1425
- .header-sticky-intersection {
1426
- background-color: var(
1427
- --grav-crud-color-bg,
1428
- rgba(255, 255, 255, 0.95)
1429
- ) !important;
1430
- backdrop-filter: none !important;
1431
- -webkit-backdrop-filter: none !important;
1432
- }
1433
-
1434
- /* When header-sticky-intersection is applied to first column header (not drag-header) */
1435
- .borderleft.header-sticky-intersection {
1436
- position: sticky;
1437
- left: 0;
1438
- z-index: 25;
1439
- padding-right: 1rem;
1440
- margin-right: 0.5rem;
1441
- box-shadow:
1442
- inset -1.5px 0 0 var(--grav-crud-color-border),
1443
- 2px 0 6px rgba(0, 0, 0, 0.08);
1444
- }
1445
-
1446
- .sort-icon {
1447
- display: flex;
1448
- align-items: center;
1449
- justify-content: center;
1450
- }
1451
-
1452
- .sort-arrow {
1453
- margin-left: 0.25rem;
1454
- }
1455
-
1456
- .table-row {
1457
- border-bottom-width: var(--grav-crud-cell-border-width, 1.5px);
1458
- border-bottom-style: solid;
1459
- border-bottom-color: var(--grav-crud-color-border);
1460
- cursor: pointer;
1461
- transition: background-color 0.2s;
1462
- }
1463
-
1464
- .table-row:hover {
1465
- background-color: var(--grav-crud-color-light);
1466
- }
1467
-
1468
- .table-row.selected {
1469
- background-color: var(--grav-crud-color-drag);
1470
- }
1471
-
1472
- .table-cell {
1473
- border-top: 0;
1474
- border-left: 0;
1475
- border-right: 0;
1476
- white-space: nowrap;
1477
- vertical-align: middle;
1478
- z-index: 10;
1479
- }
1480
-
1481
- .sticky-cell {
1482
- position: sticky;
1483
- left: 0;
1484
- background-color: rgba(255, 255, 255, 0.8);
1485
- backdrop-filter: blur(8px);
1486
- -webkit-backdrop-filter: blur(8px);
1487
- z-index: 15;
1488
- border-bottom: inherit;
1489
- box-shadow:
1490
- inset -1.5px 0 0 var(--grav-crud-color-border),
1491
- 2px 0 6px rgba(0, 0, 0, 0.08);
1492
- }
1493
-
1494
- @supports (backdrop-filter: blur(8px)) or
1495
- (-webkit-backdrop-filter: blur(8px)) {
1496
- .sticky-cell {
1497
- background-color: var(
1498
- --grav-crud-color-bg,
1499
- rgba(255, 255, 255, 0.85)
1500
- );
1501
- }
1502
- }
1503
-
1504
- .cell-content {
1505
- padding-left: 0.25rem;
1506
- white-space: normal;
1507
- word-break: break-word;
1508
- font-family: var(--grav-crud-cell-font-family, "mundial", sans-serif);
1509
- font-size: var(--grav-crud-cell-font-size, 0.875rem);
1510
- font-weight: var(--grav-crud-cell-font-weight, 400);
1511
- line-height: var(--grav-crud-cell-line-height, 1.5);
1512
- color: var(--grav-crud-color-neutral);
1513
- margin-left: 3px;
1514
- margin-right: 3px;
1515
- padding-right: 0.3rem;
1516
- padding-left: 0.3rem;
1517
- border-radius: 0.375rem;
1518
- }
1519
-
1520
- .cell-content.bold {
1521
- font-weight: bold;
1522
- }
1523
-
1524
- .no-data {
1525
- text-align: center;
1526
- padding: 1rem 0;
1527
- font-family: var(--grav-crud-cell-font-family, "mundial", sans-serif);
1528
- font-size: var(--grav-crud-cell-font-size, 0.875rem);
1529
- font-weight: var(--grav-crud-cell-font-weight, 400);
1530
- line-height: var(--grav-crud-cell-line-height, 1.5);
1531
- }
1532
-
1533
- .loading-container {
1534
- margin: 0 auto;
1535
- width: 100%;
1536
- padding: 1rem;
1537
- }
1538
-
1539
- .loading-animation {
1540
- display: flex;
1541
- flex-direction: column;
1542
- width: 100%;
1543
- animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
1544
- }
1545
-
1546
- .loading-line {
1547
- height: 1rem;
1548
- border-radius: 0.25rem;
1549
- background-color: var(--grav-crud-color-border);
1550
- margin-bottom: 0.5rem;
1551
- width: 100%;
1552
- margin: 0 0 0.5rem 0;
1553
- flex: none;
1554
- }
1555
-
1556
- @keyframes pulse {
1557
- 0%,
1558
- 100% {
1559
- opacity: 1;
1560
- }
1561
- 50% {
1562
- opacity: 0.5;
1563
- }
1564
- }
1565
-
1566
- @media (min-width: 640px) {
1567
- .cell-content {
1568
- font-size: 1rem;
1569
- }
1570
- }
1571
-
1572
- .crud-image {
1573
- width: 4rem;
1574
- height: 4rem;
1575
- object-fit: cover;
1576
- margin: 0.5rem auto;
1577
- border-radius: var(--grav-crud-image-border-radius, 0.25rem);
1578
- display: block;
1579
- }
1580
-
1581
- /* Drag and drop styles */
1582
- .drag-header {
1583
- width: 40px;
1584
- text-align: center;
1585
- cursor: default;
1586
- position: sticky;
1587
- left: 0;
1588
- z-index: 25;
1589
- padding-right: 0.75rem;
1590
- box-shadow:
1591
- inset -1.5px 0 0 var(--grav-crud-color-border),
1592
- 2px 0 6px rgba(0, 0, 0, 0.08);
1593
- }
1594
-
1595
- .drag-handle-header {
1596
- display: flex;
1597
- align-items: center;
1598
- justify-content: center;
1599
- color: var(--grav-crud-color-icon);
1600
- }
1601
-
1602
- .drag-handle-cell {
1603
- width: 40px;
1604
- text-align: center;
1605
- padding: 0.5rem;
1606
- padding-right: 0.75rem;
1607
- cursor: grab;
1608
- background-color: rgba(255, 255, 255, 0.8);
1609
- backdrop-filter: blur(8px);
1610
- -webkit-backdrop-filter: blur(8px);
1611
- z-index: 15;
1612
- border-bottom: inherit;
1613
- box-shadow:
1614
- inset -1.5px 0 0 var(--grav-crud-color-border),
1615
- 2px 0 6px rgba(0, 0, 0, 0.08);
1616
- }
1617
-
1618
- @supports (backdrop-filter: blur(8px)) or
1619
- (-webkit-backdrop-filter: blur(8px)) {
1620
- .drag-handle-cell {
1621
- background-color: var(
1622
- --grav-crud-color-bg,
1623
- rgba(255, 255, 255, 0.85)
1624
- );
1625
- }
1626
- }
1627
-
1628
- .drag-handle {
1629
- display: flex;
1630
- align-items: center;
1631
- justify-content: center;
1632
- color: var(--grav-crud-color-icon);
1633
- transition: color 0.2s;
1634
- padding: 0.25rem;
1635
- }
1636
-
1637
- .draggable-row {
1638
- transition: all 0.3s ease;
1639
- }
1640
-
1641
- .draggable-row.dragging {
1642
- opacity: 0.5;
1643
- transform: scale(0.98);
1644
- background-color: var(--grav-crud-color-drag);
1645
- cursor: grabbing;
1646
- }
1647
-
1648
- .draggable-row.dragging .drag-handle {
1649
- cursor: grabbing;
1650
- }
1651
-
1652
- .draggable-row.drag-over {
1653
- background-color: var(--grav-crud-color-drag);
1654
- border-top: 2px solid var(--grav-crud-color-drag);
1655
- border-bottom: 2px solid var(--grav-crud-color-drag);
1656
- }
1657
-
1658
- /* Editable cell styles */
1659
- .editable-checkbox {
1660
- width: 2rem;
1661
- height: 2rem;
1662
- background: transparent;
1663
- border: 1.5px solid var(--grav-crud-color-neutral);
1664
- color: var(--grav-crud-color-neutral);
1665
- cursor: pointer;
1666
- border-radius: 0.375rem;
1667
- display: inline-flex;
1668
- align-items: center;
1669
- justify-content: center;
1670
- transition: all 0.2s ease;
1671
- padding: 0;
1672
- }
1673
-
1674
- .editable-checkbox:hover {
1675
- background-color: var(--grav-crud-color-light);
1676
- }
1677
-
1678
- .editable-checkbox.checked {
1679
- background-color: var(--grav-crud-color-primary);
1680
- color: var(--grav-crud-color-icon);
1681
- border-color: var(--grav-crud-color-primary);
1682
- }
1683
-
1684
- .editable-input {
1685
- width: 100%;
1686
- padding: 0.5rem;
1687
- border: 1.5px solid transparent;
1688
- background: transparent;
1689
- border-radius: 0.375rem;
1690
- font-family: var(--grav-crud-cell-font-family, "mundial", sans-serif);
1691
- font-size: var(--grav-crud-cell-font-size, 0.875rem);
1692
- color: var(--grav-crud-color-neutral);
1693
- transition: all 0.2s ease;
1694
- }
1695
-
1696
- .editable-input:hover {
1697
- border-color: var(--grav-crud-color-border);
1698
- background-color: var(--grav-crud-color-light);
1699
- }
1700
-
1701
- .editable-input:focus {
1702
- outline: none;
1703
- border-color: var(--grav-crud-color-primary);
1704
- background-color: var(--grav-crud-color-bg);
1705
- }
1706
-
1707
- /* Remove number input arrows */
1708
- .editable-input[type="number"]::-webkit-outer-spin-button,
1709
- .editable-input[type="number"]::-webkit-inner-spin-button {
1710
- -webkit-appearance: none;
1711
- margin: 0;
1712
- }
1713
-
1714
- .editable-input[type="number"] {
1715
- -moz-appearance: textfield;
1716
- }
1717
-
1718
- /* Dynamic Button Styles */
1719
- .dynamic-button {
1720
- display: flex;
1721
- align-items: center;
1722
- justify-content: center;
1723
- gap: 0.5rem;
1724
- padding: 0.5rem 1rem;
1725
- border-radius: 0.375rem;
1726
- font-family: var(--grav-crud-cell-font-family, "mundial", sans-serif);
1727
- font-size: var(--grav-crud-cell-font-size, 0.875rem);
1728
- font-weight: 500;
1729
- border: 1.5px solid transparent;
1730
- cursor: pointer;
1731
- transition: all 0.2s ease;
1732
- white-space: nowrap;
1733
- background-color: var(--grav-crud-color-neutral);
1734
- color: var(--grav-crud-color-button);
1735
- }
1736
-
1737
- .dynamic-button:hover {
1738
- opacity: 0.8;
1739
- transform: translateY(-1px);
1740
- }
1741
-
1742
- .dynamic-button:active {
1743
- transform: translateY(0);
1744
- }
1745
-
1746
- .dynamic-button-icon-left {
1747
- }
1748
-
1749
- .dynamic-button-icon-right {
1750
- }
1751
-
1752
- /* Image Button Styles */
1753
- .image-button-wrapper {
1754
- background: transparent;
1755
- border: none;
1756
- padding: 0;
1757
- cursor: pointer;
1758
- display: inline-flex;
1759
- align-items: center;
1760
- justify-content: center;
1761
- }
1762
-
1763
- .image-button {
1764
- border-radius: 50%;
1765
- object-fit: cover;
1766
- transition: all 0.2s ease;
1767
- border: none;
1768
- }
1769
-
1770
- .image-button:hover {
1771
- transform: scale(1.1);
1772
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
1773
- }
1774
-
1775
- .image-button:active {
1776
- transform: scale(1.05);
1777
- }
1778
-
1779
- .image-button-sm {
1780
- width: 32px;
1781
- height: 32px;
1782
- }
1783
-
1784
- .image-button-md {
1785
- width: 48px;
1786
- height: 48px;
1787
- }
1788
-
1789
- .image-button-lg {
1790
- width: 64px;
1791
- height: 64px;
1792
- }
1793
-
1794
- /* Expand/Collapse Styles */
1795
- .expand-header {
1796
- width: 50px;
1797
- text-align: center;
1798
- }
1799
-
1800
- .expand-cell {
1801
- width: 50px;
1802
- text-align: center;
1803
- padding: 0.5rem;
1804
- vertical-align: middle;
1805
- }
1806
-
1807
- .expand-button {
1808
- background: transparent;
1809
- border: none;
1810
- cursor: pointer;
1811
- padding: 0.25rem;
1812
- display: inline-flex;
1813
- align-items: center;
1814
- justify-content: center;
1815
- border-radius: 0.25rem;
1816
- transition: all 0.2s ease;
1817
- }
1818
-
1819
- .expand-button:hover {
1820
- background-color: var(--grav-crud-color-light);
1821
- }
1822
-
1823
- .chevron-icon {
1824
- color: var(--grav-crud-color-neutral);
1825
- transition: transform 0.3s ease;
1826
- transform: rotate(0deg);
1827
- }
1828
-
1829
- .chevron-icon.expanded {
1830
- transform: rotate(90deg);
1831
- }
1832
-
1833
- .expand-button:hover .chevron-icon {
1834
- color: var(--grav-crud-color-primary, var(--grav-crud-color-neutral));
1835
- transform: scale(1.15) rotate(0deg);
1836
- }
1837
-
1838
- .expand-button:hover .chevron-icon.expanded {
1839
- transform: scale(1.15) rotate(90deg);
1840
- }
1841
-
1842
- /* Subrow Styles */
1843
- .sub-row-container {
1844
- background-color: rgba(0, 0, 0, 0.02);
1845
- }
1846
-
1847
- .sub-row-cell {
1848
- padding: 0 !important;
1849
- }
1850
-
1851
- .sub-table {
1852
- width: 100%;
1853
- border-collapse: collapse;
1854
- margin: 0;
1855
- background-color: rgba(0, 0, 0, 0.02);
1856
- }
1857
-
1858
- .sub-table .sub-row {
1859
- border-bottom: 1px solid rgba(0, 0, 0, 0.05);
1860
- }
1861
-
1862
- .sub-table .sub-row:hover {
1863
- background-color: rgba(0, 0, 0, 0.04);
1864
- }
1865
-
1866
- .sub-table .table-cell {
1867
- padding: 0.5rem;
1868
- vertical-align: middle;
1869
- border: none;
1870
- }
1871
-
1872
- .sub-table .cell-content {
1873
- padding-left: 0.5rem;
1874
- }
1875
-
1876
- /* Dual Text Button Styles */
1877
- .dual-text-button {
1878
- display: flex;
1879
- flex-direction: column;
1880
- align-items: center;
1881
- justify-content: center;
1882
- cursor: pointer;
1883
- }
1884
-
1885
- .dual-text-button:hover {
1886
- border-color: var(--grav-crud-color-primary);
1887
- transform: translateY(-1px);
1888
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
1889
- }
1890
-
1891
- .dual-text-button:active {
1892
- transform: translateY(0);
1893
- }
1894
-
1895
- .dual-text-1 {
1896
- font-weight: 600;
1897
- width: 100%;
1898
- padding: 0.25rem 0.5rem;
1899
- border-top-left-radius: var(--grav-border-radius-md);
1900
- border-top-right-radius: var(--grav-border-radius-md);
1901
- }
1902
-
1903
- .dual-text-2 {
1904
- font-weight: 600;
1905
- width: 100%;
1906
- padding: 0.25rem 0.5rem;
1907
- border-bottom-left-radius: var(--grav-border-radius-md);
1908
- border-bottom-right-radius: var(--grav-border-radius-md);
1909
- }
1910
-
1911
- .dual-text-separator {
1912
- color: var(--grav-crud-color-neutral);
1913
- opacity: 0.5;
1914
- font-weight: 400;
1915
- }
1916
-
1917
- /* Multi Text Button Styles */
1918
- .multi-text-button {
1919
- display: flex;
1920
- cursor: pointer;
1921
- transition: all 0.2s ease;
1922
- border: none;
1923
- background: transparent;
1924
- padding: 0.25rem;
1925
- width: 100%;
1926
- }
1927
-
1928
- .multi-text-button:hover {
1929
- transform: translateY(-1px);
1930
- }
1931
-
1932
- .multi-text-button:active {
1933
- transform: translateY(0);
1934
- }
1935
-
1936
- .multi-text-vertical {
1937
- flex-direction: column;
1938
- gap: 0.25rem;
1939
- align-items: center;
1940
- justify-content: center;
1941
- }
1942
-
1943
- .multi-text-horizontal {
1944
- flex-direction: row;
1945
- gap: 0.5rem;
1946
- flex-wrap: wrap;
1947
- align-items: center;
1948
- justify-content: center;
1949
- }
1950
-
1951
- .multi-text-item {
1952
- font-weight: 600;
1953
- padding: 0.25rem 0.5rem;
1954
- border-radius: var(--grav-border-radius-md);
1955
- font-size: 0.875rem;
1956
- white-space: nowrap;
1957
- transition: all 0.2s ease;
1958
- text-align: center;
1959
- }
1960
-
1961
- .multi-text-vertical .multi-text-item {
1962
- min-width: 120px;
1963
- }
1964
-
1965
- .multi-text-vertical .multi-text-item:first-child {
1966
- border-top-left-radius: var(--grav-border-radius-md);
1967
- border-top-right-radius: var(--grav-border-radius-md);
1968
- }
1969
-
1970
- .multi-text-vertical .multi-text-item:last-child {
1971
- border-bottom-left-radius: var(--grav-border-radius-md);
1972
- border-bottom-right-radius: var(--grav-border-radius-md);
1973
- }
1974
-
1975
- .multi-text-separator {
1976
- color: var(--grav-crud-color-neutral);
1977
- opacity: 0.5;
1978
- font-weight: 400;
1979
- }
1980
- </style>