@edc-motor/admin-kit 0.4.16 → 0.4.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edc-motor/admin-kit",
3
- "version": "0.4.16",
3
+ "version": "0.4.17",
4
4
  "description": "EdC Motor — kit del panel de administración: layout, gestores CRUD, imágenes, PDF, páginas, configuración, usuarios y copias (paquete fuente: lo compila el consumidor con Vite)",
5
5
  "license": "GPL-3.0-only",
6
6
  "type": "module",
@@ -33,7 +33,7 @@
33
33
  "access": "public"
34
34
  },
35
35
  "dependencies": {
36
- "@edc-motor/ui": "^0.4.16",
36
+ "@edc-motor/ui": "^0.4.17",
37
37
  "axios": "^1.7.0",
38
38
  "vue-draggable-plus": "^0.6.1"
39
39
  },
@@ -67,6 +67,13 @@
67
67
  padding: $space-3 $space-4 $space-2;
68
68
  border-bottom: 1px solid $border;
69
69
  min-width: 0;
70
+
71
+ // Sin badges ni meta la cabecera es lo último de la card: la divisoria
72
+ // sobra y el padding inferior recupera el del resto de lados.
73
+ &:last-child {
74
+ border-bottom: none;
75
+ padding-bottom: $space-3;
76
+ }
70
77
  }
71
78
 
72
79
  &__title {
@@ -75,6 +75,12 @@
75
75
  padding: $space-3 $space-4 $space-2;
76
76
  border-bottom: 1px solid $border;
77
77
  min-width: 0;
78
+
79
+ // Card sin nada debajo de la cabecera: ni divisoria ni padding cojo.
80
+ &:last-child {
81
+ border-bottom: none;
82
+ padding-bottom: $space-3;
83
+ }
78
84
  }
79
85
 
80
86
  &__title {
@@ -1,6 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  import { PanelRight } from '@lucide/vue'
3
3
 
4
+ import { slotHasContent } from '../composables/slotHasContent'
5
+
4
6
  // Tarjeta de los gestores del admin (previews y PDF): NO colapsa. Mismo
5
7
  // lenguaje visual que EntityCard: cabecera con el título (+ chip opcional) y
6
8
  // divisoria; debajo, las BADGES (chips de estado, slot badges) y el meta
@@ -44,21 +46,22 @@ function onClick(event: MouseEvent) {
44
46
  <PanelRight :size="16" class="manager-card__hint" />
45
47
  </div>
46
48
 
47
- <!-- Como EntityCard: badges (chips) arriba, meta debajo -->
48
- <div v-if="$slots.badges || $slots.meta" class="manager-card__content">
49
- <div v-if="$slots.badges" class="manager-card__badges">
49
+ <!-- Como EntityCard: badges (chips) arriba, meta debajo. slotHasContent
50
+ y no $slots.x: sin contenido real, sin parte inferior vacía. -->
51
+ <div v-if="slotHasContent($slots.badges) || slotHasContent($slots.meta)" class="manager-card__content">
52
+ <div v-if="slotHasContent($slots.badges)" class="manager-card__badges">
50
53
  <slot name="badges" />
51
54
  </div>
52
- <div v-if="$slots.meta" class="manager-card__meta">
55
+ <div v-if="slotHasContent($slots.meta)" class="manager-card__meta">
53
56
  <slot name="meta" />
54
57
  </div>
55
58
  </div>
56
59
 
57
- <div v-if="$slots.default" class="manager-card__body">
60
+ <div v-if="slotHasContent($slots.default)" class="manager-card__body">
58
61
  <slot />
59
62
  </div>
60
63
 
61
- <footer v-if="$slots.actions" class="manager-card__actions">
64
+ <footer v-if="slotHasContent($slots.actions)" class="manager-card__actions">
62
65
  <slot name="actions" />
63
66
  </footer>
64
67
  </section>
@@ -0,0 +1,24 @@
1
+ import { Comment, Fragment, Text, type Slot, type VNode } from 'vue'
2
+
3
+ /**
4
+ * ¿El slot pinta algo de verdad? `$slots.x` es truthy en cuanto el padre
5
+ * declara el `<template #x>`, aunque dentro todo sea un v-if falso o un
6
+ * v-for vacío; evaluando los vnodes evitamos pintar contenedores vacíos
7
+ * (la parte inferior de las cards, con su padding y su divisoria).
8
+ */
9
+ export function slotHasContent(slot?: Slot): boolean {
10
+ return slot ? vnodesHaveContent(slot()) : false
11
+ }
12
+
13
+ function vnodesHaveContent(nodes: VNode[]): boolean {
14
+ return nodes.some((node) => {
15
+ if (node.type === Comment) return false
16
+ if (node.type === Text) {
17
+ return typeof node.children === 'string' && node.children.trim() !== ''
18
+ }
19
+ if (node.type === Fragment) {
20
+ return Array.isArray(node.children) && vnodesHaveContent(node.children as VNode[])
21
+ }
22
+ return true
23
+ })
24
+ }
@@ -7,6 +7,8 @@
7
7
  // solo para entidades con imagen/preview — sin imagen, sin franja.
8
8
  import { SquarePen } from '@lucide/vue'
9
9
 
10
+ import { slotHasContent } from '../composables/slotHasContent'
11
+
10
12
  withDefaults(
11
13
  defineProps<{
12
14
  title: string
@@ -50,7 +52,7 @@ defineSlots<{
50
52
  :style="accentColor ? { '--entity-card-accent': accentColor } : undefined"
51
53
  @click="clickable ? $emit('view') : undefined"
52
54
  >
53
- <div v-if="$slots.media" class="entity-card__media"><slot name="media" /></div>
55
+ <div v-if="slotHasContent($slots.media)" class="entity-card__media"><slot name="media" /></div>
54
56
 
55
57
  <div class="entity-card__header">
56
58
  <h3 class="entity-card__title">{{ title }}</h3>
@@ -69,9 +71,14 @@ defineSlots<{
69
71
  </div>
70
72
  </div>
71
73
 
72
- <div v-if="$slots.badges || $slots.meta || $slots.default" class="entity-card__content">
73
- <div v-if="$slots.badges" class="entity-card__badges"><slot name="badges" /></div>
74
- <div v-if="$slots.meta" class="entity-card__meta"><slot name="meta" /></div>
74
+ <!-- slotHasContent y no $slots.x: con el template declarado pero todo
75
+ v-if falso dentro, la card no debe pintar la parte inferior vacía -->
76
+ <div
77
+ v-if="slotHasContent($slots.badges) || slotHasContent($slots.meta) || slotHasContent($slots.default)"
78
+ class="entity-card__content"
79
+ >
80
+ <div v-if="slotHasContent($slots.badges)" class="entity-card__badges"><slot name="badges" /></div>
81
+ <div v-if="slotHasContent($slots.meta)" class="entity-card__meta"><slot name="meta" /></div>
75
82
  <slot />
76
83
  </div>
77
84
  </div>