grav-svelte 0.0.61 → 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 (43) hide show
  1. package/dist/CRUD/CrudTable.svelte +62 -1590
  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/interfaces.d.ts +52 -1
  42. package/dist/CRUD/styles/CrudTable.css +661 -0
  43. 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,637 +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
- {/if}
806
- {/each}
807
- </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
+ />
808
188
 
809
189
  <!-- Subrows as nested table -->
810
- {#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}
811
191
  <tr class="sub-row-container">
812
192
  <td
813
193
  colspan={tableHeaders.length +
@@ -815,320 +195,12 @@
815
195
  (expandEnabled ? 1 : 0)}
816
196
  class="sub-row-cell"
817
197
  >
818
- <table class="sub-table">
819
- <tbody>
820
- {#each item[subRowsField] as subItem, subIndex}
821
- <tr class="sub-row">
822
- {#each effectiveSubRowHeaders as subHeader, i}
823
- {#if subHeader.tipo == "Text"}
824
- <td
825
- class="table-cell"
826
- >
827
- <p
828
- class="cell-content {subItem[
829
- subHeader.colorCampo ??
830
- ''
831
- ] ??
832
- ''} {subHeader.biBold
833
- ? 'bold'
834
- : ''}"
835
- style="text-align: {subHeader.align ??
836
- 'left'}"
837
- >
838
- {subItem[
839
- subHeader
840
- .campo
841
- ] ?? ""}
842
- </p>
843
- </td>
844
- {:else if subHeader.tipo == "Number"}
845
- <td
846
- class="table-cell"
847
- >
848
- <p
849
- class="cell-content {subItem[
850
- subHeader.colorCampo ??
851
- ''
852
- ] ??
853
- ''} {subHeader.biBold
854
- ? 'bold'
855
- : ''}"
856
- style="text-align: {subHeader.align ??
857
- 'left'}"
858
- >
859
- {subItem[
860
- subHeader
861
- .campo
862
- ] ?? ""}
863
- </p>
864
- </td>
865
- {:else if subHeader.tipo == "Date"}
866
- <td
867
- class="table-cell"
868
- >
869
- <p
870
- class="cell-content {subItem[
871
- subHeader.colorCampo ??
872
- ''
873
- ] ??
874
- ''} {subHeader.biBold
875
- ? 'bold'
876
- : ''}"
877
- style="text-align: {subHeader.align ??
878
- 'left'}"
879
- >
880
- {subItem[
881
- subHeader
882
- .campo
883
- ]?.split(
884
- "T"
885
- )[0] ?? ":"}
886
- </p>
887
- </td>
888
- {:else if subHeader.tipo == "Datetime"}
889
- <td
890
- class="table-cell"
891
- >
892
- <p
893
- class="cell-content {subItem[
894
- subHeader.colorCampo ??
895
- ''
896
- ] ??
897
- ''} {subHeader.biBold
898
- ? 'bold'
899
- : ''}"
900
- style="text-align: {subHeader.align ??
901
- 'left'}"
902
- >
903
- {subItem[
904
- subHeader
905
- .campo
906
- ]?.replace(
907
- "T",
908
- ":"
909
- ) ?? ":"}
910
- </p>
911
- </td>
912
- {:else if subHeader.tipo == "Bool"}
913
- <td
914
- class="table-cell"
915
- >
916
- {#if subItem[subHeader.campo] === true}
917
- <p
918
- class="cell-content {subItem[
919
- subHeader.colorCampo ??
920
- ''
921
- ] ??
922
- ''} {subHeader.biBold
923
- ? 'bold'
924
- : ''}"
925
- style="text-align: {subHeader.align ??
926
- 'left'}"
927
- >
928
- <i
929
- class="fas fa-check"
930
- ></i>
931
- </p>
932
- {:else}
933
- <p
934
- class="cell-content {subItem[
935
- subHeader.colorCampo ??
936
- ''
937
- ] ??
938
- ''} {subHeader.biBold
939
- ? 'bold'
940
- : ''}"
941
- style="text-align: {subHeader.align ??
942
- 'left'}"
943
- >
944
- <i
945
- class="fas fa-minus"
946
- ></i>
947
- </p>
948
- {/if}
949
- </td>
950
- {:else if subHeader.tipo == "Image"}
951
- <td
952
- class="table-cell"
953
- >
954
- <img
955
- class="crud-image cursor-pointer"
956
- src={subItem[
957
- subHeader
958
- .campo
959
- ] ?? ""}
960
- alt="image"
961
- on:click={() =>
962
- openImageModal(
963
- subItem[
964
- subHeader
965
- .campo
966
- ]
967
- )}
968
- />
969
- </td>
970
- {:else if subHeader.tipo == "Buttons"}
971
- <CrudTableButtons
972
- id={subItem[
973
- subHeader
974
- .campo
975
- ]}
976
- buttonsConfig={subHeader.buttonsConfig ??
977
- []}
978
- align={subHeader.align ??
979
- "center"}
980
- />
981
- {:else if subHeader.tipo == "DynamicButton"}
982
- <td
983
- class="table-cell"
984
- style="text-align: {subHeader.align ??
985
- 'center'}"
986
- >
987
- <button
988
- type="button"
989
- class="dynamic-button {subItem[
990
- subHeader.colorField ??
991
- ''
992
- ] ?? ''}"
993
- on:click={() => {
994
- if (
995
- subHeader.onButtonClick
996
- ) {
997
- subHeader.onButtonClick(
998
- subItem[
999
- idField
1000
- ],
1001
- subItem
1002
- );
1003
- }
1004
- }}
1005
- >
1006
- {#if subHeader.iconField && subItem[subHeader.iconField]}
1007
- {#if !subHeader.iconPosition || subHeader.iconPosition === "left"}
1008
- <i
1009
- class="{subItem[
1010
- subHeader
1011
- .iconField
1012
- ]} dynamic-button-icon-left"
1013
-
1014
- ></i>
1015
- {/if}
1016
- {/if}
1017
- {#if subHeader.textField && subItem[subHeader.textField]}
1018
- <span
1019
- >{subItem[
1020
- subHeader
1021
- .textField
1022
- ]}</span
1023
- >
1024
- {/if}
1025
- {#if subHeader.iconField && subItem[subHeader.iconField]}
1026
- {#if subHeader.iconPosition === "right"}
1027
- <i
1028
- class="{subItem[
1029
- subHeader
1030
- .iconField
1031
- ]} dynamic-button-icon-right"
1032
-
1033
- ></i>
1034
- {/if}
1035
- {/if}
1036
- </button>
1037
- </td>
1038
- {:else if subHeader.tipo == "ImageButton"}
1039
- <td
1040
- class="table-cell"
1041
- style="text-align: {subHeader.align ??
1042
- 'center'}"
1043
- >
1044
- <button
1045
- type="button"
1046
- class="image-button-wrapper"
1047
- on:click={() => {
1048
- if (
1049
- subHeader.action
1050
- ) {
1051
- subHeader.action(
1052
- subItem[
1053
- idField
1054
- ]
1055
- );
1056
- }
1057
- }}
1058
- >
1059
- <img
1060
- src={subItem[
1061
- subHeader.imageField ??
1062
- ""
1063
- ] ?? ""}
1064
- alt="button"
1065
- class="image-button image-button-{subHeader.imageSize ??
1066
- 'md'}"
1067
- />
1068
- </button>
1069
- </td>
1070
- {:else if subHeader.tipo == "DualTextButton"}
1071
- <td
1072
- class="table-cell"
1073
- style="text-align: {subHeader.align ??
1074
- 'center'}"
1075
- >
1076
- <button
1077
- type="button"
1078
- class="dual-text-button"
1079
- on:click={() => {
1080
- if (
1081
- subHeader.onButtonClick
1082
- ) {
1083
- subHeader.onButtonClick(
1084
- subItem[
1085
- idField
1086
- ],
1087
- subItem
1088
- );
1089
- }
1090
- }}
1091
- >
1092
- {#if subHeader.textField1 && subItem[subHeader.textField1]}
1093
- <span
1094
- class="dual-text-1 {subItem[
1095
- subHeader.colorField1 ??
1096
- ''
1097
- ] ??
1098
- ''}"
1099
- >
1100
- {subItem[
1101
- subHeader
1102
- .textField1
1103
- ]}
1104
- </span>
1105
- {/if}
1106
- {#if subHeader.textField2 && subItem[subHeader.textField2]}
1107
- <span
1108
- class="dual-text-2 {subItem[
1109
- subHeader.colorField2 ??
1110
- ''
1111
- ] ??
1112
- ''}"
1113
- >
1114
- {subItem[
1115
- subHeader
1116
- .textField2
1117
- ]}
1118
- </span>
1119
- {/if}
1120
- </button>
1121
- </td>
1122
- {:else}
1123
- <td
1124
- class="table-cell"
1125
- ></td>
1126
- {/if}
1127
- {/each}
1128
- </tr>
1129
- {/each}
1130
- </tbody>
1131
- </table>
198
+ <SubRowsTable
199
+ subRows={item[subRowsField]}
200
+ subRowHeaders={effectiveSubRowHeaders}
201
+ {idField}
202
+ onImageClick={openImageModal}
203
+ />
1132
204
  </td>
1133
205
  </tr>
1134
206
  {/if}
@@ -1172,603 +244,3 @@
1172
244
  {/if}
1173
245
  </div>
1174
246
  </div>
1175
-
1176
- <style>
1177
- .table-container {
1178
- position: relative;
1179
- display: flex;
1180
- flex-direction: column;
1181
- min-width: 0;
1182
- word-break: break-word;
1183
- z-index: 30;
1184
- width: 100%;
1185
- margin-bottom: 0.75rem;
1186
- margin-top: 0.75rem;
1187
- }
1188
-
1189
- .table-scroll {
1190
- overflow: auto;
1191
- max-height: 80vh;
1192
- }
1193
-
1194
- /* Scrollbar Styles for WebKit browsers (Chrome, Safari, Edge) */
1195
- .table-scroll::-webkit-scrollbar {
1196
- width: var(--grav-crud-scrollbar-width);
1197
- height: var(--grav-crud-scrollbar-width);
1198
- }
1199
-
1200
- .table-scroll::-webkit-scrollbar-track {
1201
- background: var(--grav-crud-scrollbar-track);
1202
- border-radius: var(--grav-border-radius-sm);
1203
- }
1204
-
1205
- .table-scroll::-webkit-scrollbar-thumb {
1206
- background: var(--grav-crud-scrollbar-thumb);
1207
- border-radius: var(--grav-border-radius-sm);
1208
- transition: background-color 0.2s ease;
1209
- }
1210
-
1211
- .table-scroll::-webkit-scrollbar-thumb:hover {
1212
- background: var(--grav-crud-scrollbar-thumb-hover);
1213
- }
1214
-
1215
- /* Scrollbar Styles for Firefox */
1216
- .table-scroll {
1217
- scrollbar-width: thin;
1218
- scrollbar-color: var(--grav-crud-scrollbar-thumb)
1219
- var(--grav-crud-scrollbar-track);
1220
- }
1221
-
1222
- .data-table {
1223
- width: 100%;
1224
- border-collapse: collapse;
1225
- table-layout: auto;
1226
- }
1227
-
1228
- .table-header {
1229
- position: sticky;
1230
- top: 0;
1231
- z-index: 20;
1232
- border-bottom-style: solid;
1233
- border-bottom-color: var(--grav-crud-color-border);
1234
- border-bottom-width: var(--grav-crud-cell-border-width, 1.5px);
1235
- background-color: rgba(255, 255, 255, 0.8);
1236
- backdrop-filter: blur(8px);
1237
- -webkit-backdrop-filter: blur(8px);
1238
- box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
1239
- }
1240
-
1241
- @supports (backdrop-filter: blur(8px)) or
1242
- (-webkit-backdrop-filter: blur(8px)) {
1243
- .table-header {
1244
- background-color: var(
1245
- --grav-crud-color-bg,
1246
- rgba(255, 255, 255, 0.85)
1247
- );
1248
- }
1249
- }
1250
-
1251
- .table-header-cell {
1252
- padding: 0.75rem;
1253
- text-align: center;
1254
- padding-top: 0.25rem;
1255
- padding-bottom: 0.25rem;
1256
- font-family: var(--grav-crud-header-font-family, "mundial", sans-serif);
1257
- font-size: var(--grav-crud-header-font-size, 0.75rem);
1258
- font-weight: var(--grav-crud-header-font-weight, 400);
1259
- line-height: var(--grav-crud-header-line-height, 1.5);
1260
- text-transform: uppercase;
1261
- white-space: nowrap;
1262
- background-color: inherit;
1263
- color: var(--grav-crud-color-neutral);
1264
- border-left: 0;
1265
- }
1266
-
1267
- .non-sortable {
1268
- cursor: not-allowed;
1269
- }
1270
-
1271
- .sortable {
1272
- cursor: pointer;
1273
- text-align: left;
1274
- }
1275
-
1276
- .borderleft {
1277
- border-left-width: var(--grav-crud-table-border-width, 1.5px);
1278
- border-left-style: solid;
1279
- border-left-color: var(--grav-crud-color-border);
1280
- }
1281
-
1282
- /* Special class for header cells that are sticky on both axes (top + left) */
1283
- .header-sticky-intersection {
1284
- background-color: var(
1285
- --grav-crud-color-bg,
1286
- rgba(255, 255, 255, 0.95)
1287
- ) !important;
1288
- backdrop-filter: none !important;
1289
- -webkit-backdrop-filter: none !important;
1290
- }
1291
-
1292
- /* When header-sticky-intersection is applied to first column header (not drag-header) */
1293
- .borderleft.header-sticky-intersection {
1294
- position: sticky;
1295
- left: 0;
1296
- z-index: 25;
1297
- padding-right: 1rem;
1298
- margin-right: 0.5rem;
1299
- box-shadow:
1300
- inset -1.5px 0 0 var(--grav-crud-color-border),
1301
- 2px 0 6px rgba(0, 0, 0, 0.08);
1302
- }
1303
-
1304
- .sort-icon {
1305
- display: flex;
1306
- align-items: center;
1307
- justify-content: center;
1308
- }
1309
-
1310
- .sort-arrow {
1311
- margin-left: 0.25rem;
1312
- }
1313
-
1314
- .table-row {
1315
- border-bottom-width: var(--grav-crud-cell-border-width, 1.5px);
1316
- border-bottom-style: solid;
1317
- border-bottom-color: var(--grav-crud-color-border);
1318
- cursor: pointer;
1319
- transition: background-color 0.2s;
1320
- }
1321
-
1322
- .table-row:hover {
1323
- background-color: var(--grav-crud-color-light);
1324
- }
1325
-
1326
- .table-row.selected {
1327
- background-color: var(--grav-crud-color-drag);
1328
- }
1329
-
1330
- .table-cell {
1331
- border-top: 0;
1332
- border-left: 0;
1333
- border-right: 0;
1334
- white-space: nowrap;
1335
- vertical-align: middle;
1336
- z-index: 10;
1337
- }
1338
-
1339
- .sticky-cell {
1340
- position: sticky;
1341
- left: 0;
1342
- background-color: rgba(255, 255, 255, 0.8);
1343
- backdrop-filter: blur(8px);
1344
- -webkit-backdrop-filter: blur(8px);
1345
- z-index: 15;
1346
- border-bottom: inherit;
1347
- box-shadow:
1348
- inset -1.5px 0 0 var(--grav-crud-color-border),
1349
- 2px 0 6px rgba(0, 0, 0, 0.08);
1350
- }
1351
-
1352
- @supports (backdrop-filter: blur(8px)) or
1353
- (-webkit-backdrop-filter: blur(8px)) {
1354
- .sticky-cell {
1355
- background-color: var(
1356
- --grav-crud-color-bg,
1357
- rgba(255, 255, 255, 0.85)
1358
- );
1359
- }
1360
- }
1361
-
1362
- .cell-content {
1363
- padding-left: 0.25rem;
1364
- white-space: normal;
1365
- word-break: break-word;
1366
- font-family: var(--grav-crud-cell-font-family, "mundial", sans-serif);
1367
- font-size: var(--grav-crud-cell-font-size, 0.875rem);
1368
- font-weight: var(--grav-crud-cell-font-weight, 400);
1369
- line-height: var(--grav-crud-cell-line-height, 1.5);
1370
- color: var(--grav-crud-color-neutral);
1371
- margin-left: 3px;
1372
- margin-right: 3px;
1373
- padding-right: 0.3rem;
1374
- padding-left: 0.3rem;
1375
- border-radius: 0.375rem;
1376
- }
1377
-
1378
- .cell-content.bold {
1379
- font-weight: bold;
1380
- }
1381
-
1382
- .no-data {
1383
- text-align: center;
1384
- padding: 1rem 0;
1385
- font-family: var(--grav-crud-cell-font-family, "mundial", sans-serif);
1386
- font-size: var(--grav-crud-cell-font-size, 0.875rem);
1387
- font-weight: var(--grav-crud-cell-font-weight, 400);
1388
- line-height: var(--grav-crud-cell-line-height, 1.5);
1389
- }
1390
-
1391
- .loading-container {
1392
- margin: 0 auto;
1393
- width: 100%;
1394
- padding: 1rem;
1395
- }
1396
-
1397
- .loading-animation {
1398
- display: flex;
1399
- flex-direction: column;
1400
- width: 100%;
1401
- animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
1402
- }
1403
-
1404
- .loading-line {
1405
- height: 1rem;
1406
- border-radius: 0.25rem;
1407
- background-color: var(--grav-crud-color-border);
1408
- margin-bottom: 0.5rem;
1409
- width: 100%;
1410
- margin: 0 0 0.5rem 0;
1411
- flex: none;
1412
- }
1413
-
1414
- @keyframes pulse {
1415
- 0%,
1416
- 100% {
1417
- opacity: 1;
1418
- }
1419
- 50% {
1420
- opacity: 0.5;
1421
- }
1422
- }
1423
-
1424
- @media (min-width: 640px) {
1425
- .cell-content {
1426
- font-size: 1rem;
1427
- }
1428
- }
1429
-
1430
- .crud-image {
1431
- width: 4rem;
1432
- height: 4rem;
1433
- object-fit: cover;
1434
- margin: 0.5rem auto;
1435
- border-radius: var(--grav-crud-image-border-radius, 0.25rem);
1436
- display: block;
1437
- }
1438
-
1439
- /* Drag and drop styles */
1440
- .drag-header {
1441
- width: 40px;
1442
- text-align: center;
1443
- cursor: default;
1444
- position: sticky;
1445
- left: 0;
1446
- z-index: 25;
1447
- padding-right: 0.75rem;
1448
- box-shadow:
1449
- inset -1.5px 0 0 var(--grav-crud-color-border),
1450
- 2px 0 6px rgba(0, 0, 0, 0.08);
1451
- }
1452
-
1453
- .drag-handle-header {
1454
- display: flex;
1455
- align-items: center;
1456
- justify-content: center;
1457
- color: var(--grav-crud-color-icon);
1458
- }
1459
-
1460
- .drag-handle-cell {
1461
- width: 40px;
1462
- text-align: center;
1463
- padding: 0.5rem;
1464
- padding-right: 0.75rem;
1465
- cursor: grab;
1466
- background-color: rgba(255, 255, 255, 0.8);
1467
- backdrop-filter: blur(8px);
1468
- -webkit-backdrop-filter: blur(8px);
1469
- z-index: 15;
1470
- border-bottom: inherit;
1471
- box-shadow:
1472
- inset -1.5px 0 0 var(--grav-crud-color-border),
1473
- 2px 0 6px rgba(0, 0, 0, 0.08);
1474
- }
1475
-
1476
- @supports (backdrop-filter: blur(8px)) or
1477
- (-webkit-backdrop-filter: blur(8px)) {
1478
- .drag-handle-cell {
1479
- background-color: var(
1480
- --grav-crud-color-bg,
1481
- rgba(255, 255, 255, 0.85)
1482
- );
1483
- }
1484
- }
1485
-
1486
- .drag-handle {
1487
- display: flex;
1488
- align-items: center;
1489
- justify-content: center;
1490
- color: var(--grav-crud-color-icon);
1491
- transition: color 0.2s;
1492
- padding: 0.25rem;
1493
- }
1494
-
1495
- .draggable-row {
1496
- transition: all 0.3s ease;
1497
- }
1498
-
1499
- .draggable-row.dragging {
1500
- opacity: 0.5;
1501
- transform: scale(0.98);
1502
- background-color: var(--grav-crud-color-drag);
1503
- cursor: grabbing;
1504
- }
1505
-
1506
- .draggable-row.dragging .drag-handle {
1507
- cursor: grabbing;
1508
- }
1509
-
1510
- .draggable-row.drag-over {
1511
- background-color: var(--grav-crud-color-drag);
1512
- border-top: 2px solid var(--grav-crud-color-drag);
1513
- border-bottom: 2px solid var(--grav-crud-color-drag);
1514
- }
1515
-
1516
- /* Editable cell styles */
1517
- .editable-checkbox {
1518
- width: 2rem;
1519
- height: 2rem;
1520
- background: transparent;
1521
- border: 1.5px solid var(--grav-crud-color-neutral);
1522
- color: var(--grav-crud-color-neutral);
1523
- cursor: pointer;
1524
- border-radius: 0.375rem;
1525
- display: inline-flex;
1526
- align-items: center;
1527
- justify-content: center;
1528
- transition: all 0.2s ease;
1529
- padding: 0;
1530
- }
1531
-
1532
- .editable-checkbox:hover {
1533
- background-color: var(--grav-crud-color-light);
1534
- }
1535
-
1536
- .editable-checkbox.checked {
1537
- background-color: var(--grav-crud-color-primary);
1538
- color: var(--grav-crud-color-icon);
1539
- border-color: var(--grav-crud-color-primary);
1540
- }
1541
-
1542
- .editable-input {
1543
- width: 100%;
1544
- padding: 0.5rem;
1545
- border: 1.5px solid transparent;
1546
- background: transparent;
1547
- border-radius: 0.375rem;
1548
- font-family: var(--grav-crud-cell-font-family, "mundial", sans-serif);
1549
- font-size: var(--grav-crud-cell-font-size, 0.875rem);
1550
- color: var(--grav-crud-color-neutral);
1551
- transition: all 0.2s ease;
1552
- }
1553
-
1554
- .editable-input:hover {
1555
- border-color: var(--grav-crud-color-border);
1556
- background-color: var(--grav-crud-color-light);
1557
- }
1558
-
1559
- .editable-input:focus {
1560
- outline: none;
1561
- border-color: var(--grav-crud-color-primary);
1562
- background-color: var(--grav-crud-color-bg);
1563
- }
1564
-
1565
- /* Remove number input arrows */
1566
- .editable-input[type="number"]::-webkit-outer-spin-button,
1567
- .editable-input[type="number"]::-webkit-inner-spin-button {
1568
- -webkit-appearance: none;
1569
- margin: 0;
1570
- }
1571
-
1572
- .editable-input[type="number"] {
1573
- -moz-appearance: textfield;
1574
- }
1575
-
1576
- /* Dynamic Button Styles */
1577
- .dynamic-button {
1578
- display: flex;
1579
- align-items: center;
1580
- justify-content: center;
1581
- gap: 0.5rem;
1582
- padding: 0.5rem 1rem;
1583
- border-radius: 0.375rem;
1584
- font-family: var(--grav-crud-cell-font-family, "mundial", sans-serif);
1585
- font-size: var(--grav-crud-cell-font-size, 0.875rem);
1586
- font-weight: 500;
1587
- border: 1.5px solid transparent;
1588
- cursor: pointer;
1589
- transition: all 0.2s ease;
1590
- white-space: nowrap;
1591
- background-color: var(--grav-crud-color-neutral);
1592
- color: var(--grav-crud-color-button);
1593
- }
1594
-
1595
- .dynamic-button:hover {
1596
- opacity: 0.8;
1597
- transform: translateY(-1px);
1598
- }
1599
-
1600
- .dynamic-button:active {
1601
- transform: translateY(0);
1602
- }
1603
-
1604
- .dynamic-button-icon-left {
1605
- }
1606
-
1607
- .dynamic-button-icon-right {
1608
- }
1609
-
1610
- /* Image Button Styles */
1611
- .image-button-wrapper {
1612
- background: transparent;
1613
- border: none;
1614
- padding: 0;
1615
- cursor: pointer;
1616
- display: inline-flex;
1617
- align-items: center;
1618
- justify-content: center;
1619
- }
1620
-
1621
- .image-button {
1622
- border-radius: 50%;
1623
- object-fit: cover;
1624
- transition: all 0.2s ease;
1625
- border: none;
1626
- }
1627
-
1628
- .image-button:hover {
1629
- transform: scale(1.1);
1630
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
1631
- }
1632
-
1633
- .image-button:active {
1634
- transform: scale(1.05);
1635
- }
1636
-
1637
- .image-button-sm {
1638
- width: 32px;
1639
- height: 32px;
1640
- }
1641
-
1642
- .image-button-md {
1643
- width: 48px;
1644
- height: 48px;
1645
- }
1646
-
1647
- .image-button-lg {
1648
- width: 64px;
1649
- height: 64px;
1650
- }
1651
-
1652
- /* Expand/Collapse Styles */
1653
- .expand-header {
1654
- width: 50px;
1655
- text-align: center;
1656
- }
1657
-
1658
- .expand-cell {
1659
- width: 50px;
1660
- text-align: center;
1661
- padding: 0.5rem;
1662
- vertical-align: middle;
1663
- }
1664
-
1665
- .expand-button {
1666
- background: transparent;
1667
- border: none;
1668
- cursor: pointer;
1669
- padding: 0.25rem;
1670
- display: inline-flex;
1671
- align-items: center;
1672
- justify-content: center;
1673
- border-radius: 0.25rem;
1674
- transition: all 0.2s ease;
1675
- }
1676
-
1677
- .expand-button:hover {
1678
- background-color: var(--grav-crud-color-light);
1679
- }
1680
-
1681
- .chevron-icon {
1682
- color: var(--grav-crud-color-neutral);
1683
- transition: transform 0.3s ease;
1684
- transform: rotate(0deg);
1685
- }
1686
-
1687
- .chevron-icon.expanded {
1688
- transform: rotate(90deg);
1689
- }
1690
-
1691
- .expand-button:hover .chevron-icon {
1692
- color: var(--grav-crud-color-primary, var(--grav-crud-color-neutral));
1693
- transform: scale(1.15) rotate(0deg);
1694
- }
1695
-
1696
- .expand-button:hover .chevron-icon.expanded {
1697
- transform: scale(1.15) rotate(90deg);
1698
- }
1699
-
1700
- /* Subrow Styles */
1701
- .sub-row-container {
1702
- background-color: rgba(0, 0, 0, 0.02);
1703
- }
1704
-
1705
- .sub-row-cell {
1706
- padding: 0 !important;
1707
- }
1708
-
1709
- .sub-table {
1710
- width: 100%;
1711
- border-collapse: collapse;
1712
- margin: 0;
1713
- background-color: rgba(0, 0, 0, 0.02);
1714
- }
1715
-
1716
- .sub-table .sub-row {
1717
- border-bottom: 1px solid rgba(0, 0, 0, 0.05);
1718
- }
1719
-
1720
- .sub-table .sub-row:hover {
1721
- background-color: rgba(0, 0, 0, 0.04);
1722
- }
1723
-
1724
- .sub-table .table-cell {
1725
- padding: 0.5rem;
1726
- vertical-align: middle;
1727
- border: none;
1728
- }
1729
-
1730
- .sub-table .cell-content {
1731
- padding-left: 0.5rem;
1732
- }
1733
-
1734
- /* Dual Text Button Styles */
1735
- .dual-text-button {
1736
- display: flex;
1737
- flex-direction: column;
1738
- align-items: center;
1739
- justify-content: center;
1740
- cursor: pointer;
1741
- }
1742
-
1743
- .dual-text-button:hover {
1744
- border-color: var(--grav-crud-color-primary);
1745
- transform: translateY(-1px);
1746
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
1747
- }
1748
-
1749
- .dual-text-button:active {
1750
- transform: translateY(0);
1751
- }
1752
-
1753
- .dual-text-1 {
1754
- font-weight: 600;
1755
- width: 100%;
1756
- padding: 0.25rem 0.5rem;
1757
- border-top-left-radius: var(--grav-border-radius-md);
1758
- border-top-right-radius: var(--grav-border-radius-md);
1759
- }
1760
-
1761
- .dual-text-2 {
1762
- font-weight: 600;
1763
- width: 100%;
1764
- padding: 0.25rem 0.5rem;
1765
- border-bottom-left-radius: var(--grav-border-radius-md);
1766
- border-bottom-right-radius: var(--grav-border-radius-md);
1767
- }
1768
-
1769
- .dual-text-separator {
1770
- color: var(--grav-crud-color-neutral);
1771
- opacity: 0.5;
1772
- font-weight: 400;
1773
- }
1774
- </style>