grav-svelte 0.0.93 → 0.0.98

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.
@@ -1,10 +1,14 @@
1
1
  .alert-confirm-button {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ justify-content: center;
2
5
  background-color: #10b981;
3
6
  color: white;
4
7
  font-weight: bold;
5
8
  text-transform: uppercase;
6
9
  font-size: 0.875rem;
7
10
  padding: 0.75rem 1.5rem;
11
+ border: none;
8
12
  border-radius: 0.25rem;
9
13
  cursor: pointer;
10
14
  transition: all 150ms ease-in-out;
@@ -18,12 +22,16 @@
18
22
  }
19
23
 
20
24
  .alert-cancel-button {
25
+ display: inline-flex;
26
+ align-items: center;
27
+ justify-content: center;
21
28
  color: #EF4444;
22
29
  background: transparent;
23
30
  font-weight: bold;
24
31
  text-transform: uppercase;
25
32
  padding: 0.5rem 1.5rem;
26
33
  font-size: 0.875rem;
34
+ border: none;
27
35
  cursor: pointer;
28
36
  transition: all 150ms ease-in-out;
29
37
  }
@@ -33,6 +41,9 @@
33
41
  }
34
42
 
35
43
  .alert-error-button {
44
+ display: inline-flex;
45
+ align-items: center;
46
+ justify-content: center;
36
47
  color: #EF4444;
37
48
  background: transparent;
38
49
  font-weight: bold;
@@ -40,6 +51,7 @@
40
51
  cursor: pointer;
41
52
  padding: 0.5rem 1.5rem;
42
53
  font-size: 1rem;
54
+ border: none;
43
55
  transition: all 150ms ease-in-out;
44
56
  }
45
57
 
@@ -1,258 +1,295 @@
1
1
  .crud-filters-container {
2
- border-radius: 0.5rem;
3
- background-color: var(--grav-crud-color-bg);
4
- width: fit-content;
5
- padding: 0.5rem;
6
- box-shadow: var(--grav-crud-box-shadow);
7
- margin-bottom: 0.5rem;
8
- padding-top: 0.7rem;
9
- width: 100%;
10
-
2
+ border-radius: 0.5rem;
3
+ background-color: var(--grav-crud-color-bg);
4
+ width: fit-content;
5
+ padding: 0.5rem;
6
+ box-shadow: var(--grav-crud-box-shadow);
7
+ margin-bottom: 0.5rem;
8
+ padding-top: 0.7rem;
9
+ width: 100%;
11
10
  }
12
11
 
13
12
  @media (min-width: 640px) {
14
- .crud-filters-container {
15
- margin-bottom: 0.5rem;
16
- margin-top: 0.25rem;
17
- width: fit-content;
18
- }
13
+ .crud-filters-container {
14
+ margin-bottom: 0.5rem;
15
+ margin-top: 0.25rem;
16
+ width: fit-content;
17
+ }
19
18
  }
20
19
 
21
20
  .filters-core {
22
- display: flex;
23
- gap: 0.75rem;
24
- padding: 0.5rem;
21
+ display: flex;
22
+ gap: 0.75rem;
23
+ padding: 0.5rem;
25
24
  }
26
25
 
27
26
  @media (max-width: 640px) {
28
- .filters-core {
29
- display: flex;
30
- flex-direction: column;
31
- gap: 0.75rem;
32
- padding: 0;
33
- }
27
+ .filters-core {
28
+ display: flex;
29
+ flex-direction: column;
30
+ gap: 0.75rem;
31
+ padding: 0;
32
+ }
34
33
  }
35
34
 
36
35
  .filters-header {
37
- display: flex;
38
- align-items: center;
39
- gap: 0.75rem;
36
+ display: flex;
37
+ align-items: center;
38
+ gap: 0.75rem;
40
39
  }
41
40
 
42
41
  .filters-title {
43
- font-family: var(--grav-crud-title-font-family, 'mundial', sans-serif);
44
- font-weight: var(--grav-crud-title-font-weight, 700);
45
- font-size: var(--grav-crud-title-font-size, 1.5rem);
46
- line-height: var(--grav-crud-title-line-height, 1.25);
47
- white-space: nowrap;
48
- max-width: 100%;
49
- color: var(--grav-crud-color-neutral);
42
+ font-family: var(--grav-crud-title-font-family, "mundial", sans-serif);
43
+ font-weight: var(--grav-crud-title-font-weight, 700);
44
+ font-size: var(--grav-crud-title-font-size, 1.5rem);
45
+ line-height: var(--grav-crud-title-line-height, 1.25);
46
+ white-space: nowrap;
47
+ max-width: 100%;
48
+ color: var(--grav-crud-color-neutral);
50
49
  }
51
50
 
52
51
  .filters-actions {
53
- display: flex;
54
- justify-content: center;
55
- align-items: center;
56
- gap: 0.5rem;
57
- margin-top: 0.5rem;
52
+ display: flex;
53
+ align-items: center;
54
+ gap: 0.5rem;
55
+ }
56
+
57
+ .filters-controls .filters-actions {
58
+ justify-content: center;
59
+ margin-top: 0.5rem;
58
60
  }
59
61
 
60
62
  @media (min-width: 640px) {
61
- .filters-actions {
62
- margin-top: 0;
63
- }
63
+ .filters-controls .filters-actions {
64
+ margin-top: 0;
65
+ }
64
66
  }
65
67
 
66
68
  .filters-controls {
67
- display: flex;
68
- align-items: center;
69
-
70
- margin-top: 0.5rem;
69
+ display: flex;
70
+ align-items: center;
71
+
72
+ margin-top: 0.5rem;
71
73
  }
72
74
 
73
75
  @media (min-width: 640px) {
74
- .filters-controls {
75
- margin-top: 0;
76
- }
77
- .filters-actions {
78
- margin-right: 0.5rem;
79
- }
80
-
76
+ .filters-controls {
77
+ margin-top: 0;
78
+ }
79
+ .filters-actions {
80
+ margin-right: 0.5rem;
81
+ }
81
82
  }
82
83
 
83
84
  .tooltip-container {
84
- position: relative;
85
+ position: relative;
85
86
  }
86
87
 
87
88
  .tooltip {
88
- position: absolute;
89
- z-index: 10000;
90
- padding: 0.25rem;
91
- color: white;
92
- font-size: 0.75rem;
93
- background-color: var(--grav-crud-color-icon-hover);
94
- border-radius: var(--grav-crud-tooltip-border-radius, 0.25rem);
95
- top: -1.25rem;
96
- left: -2.5rem;
97
- pointer-events: none;
89
+ position: absolute;
90
+ z-index: 10000;
91
+ padding: 0.25rem;
92
+ color: white;
93
+ font-size: 0.75rem;
94
+ background-color: var(--grav-crud-color-icon-hover);
95
+ border-radius: var(--grav-crud-tooltip-border-radius, 0.25rem);
96
+ top: -1.25rem;
97
+ left: -2.5rem;
98
+ pointer-events: none;
98
99
  }
99
100
 
100
101
  .action-button {
101
- display: flex;
102
- justify-content: center;
103
- align-items: center;
104
- cursor: pointer;
105
- border-width: var(--grav-crud-filter-border-width, 1.5px);
106
- border-style: solid;
107
- border-color: var(--grav-crud-color-neutral);
108
- color: var(--grav-crud-color-neutral);
109
- width: 3rem;
110
- height: 2.5rem;
111
- border-radius: var(--grav-crud-filter-border-radius, 0.5rem);
102
+ display: flex;
103
+ justify-content: center;
104
+ align-items: center;
105
+ cursor: pointer;
106
+ border-width: var(--grav-crud-filter-border-width, 1.5px);
107
+ border-style: solid;
108
+ border-color: var(--grav-crud-color-neutral);
109
+ color: var(--grav-crud-color-neutral);
110
+ width: 3rem;
111
+ height: 2.5rem;
112
+ border-radius: var(--grav-crud-filter-border-radius, 0.5rem);
112
113
  }
113
114
 
114
115
  .filter-group {
115
- display: inline-flex;
116
- gap: 0.5rem;
116
+ display: inline-flex;
117
+ gap: 0.5rem;
117
118
  }
118
119
 
119
120
  @media (min-width: 640px) {
120
- .filter-group {
121
- padding: 0;
122
- gap: 0.5rem;
123
- margin-right: 0.5rem;
124
- }
121
+ .filter-group {
122
+ padding: 0;
123
+ gap: 0.5rem;
124
+ margin-right: 0.5rem;
125
+ }
125
126
  }
126
127
 
127
128
  .filter-button {
128
- display: inline-flex;
129
- align-items: center;
130
- justify-content: center;
131
- cursor: pointer;
132
- height: 2.5rem;
133
- font-family: var(--grav-crud-button-font-family, 'mundial', sans-serif);
134
- font-size: 1.1rem;
135
- font-weight: var(--grav-crud-button-font-weight, 500);
136
- line-height: var(--grav-crud-button-line-height, 1.5);
137
- border-width: var(--grav-crud-filter-border-width, 1.5px);
138
- border-style: solid;
139
- border-color: var(--grav-crud-color-neutral);
140
- color: var(--grav-crud-color-neutral);
141
- border-radius: var(--grav-crud-filter-border-radius, 0.5rem);
142
- padding: 0.75rem 1rem;
129
+ display: inline-flex;
130
+ align-items: center;
131
+ justify-content: center;
132
+ cursor: pointer;
133
+ height: 2.5rem;
134
+ font-family: var(--grav-crud-button-font-family, "mundial", sans-serif);
135
+ font-size: 1.1rem;
136
+ font-weight: var(--grav-crud-button-font-weight, 500);
137
+ line-height: var(--grav-crud-button-line-height, 1.5);
138
+ border-width: var(--grav-crud-filter-border-width, 1.5px);
139
+ border-style: solid;
140
+ border-color: var(--grav-crud-color-neutral);
141
+ color: var(--grav-crud-color-neutral);
142
+ border-radius: var(--grav-crud-filter-border-radius, 0.5rem);
143
+ padding: 0.75rem 1rem;
143
144
  }
144
145
 
145
146
  @media (min-width: 640px) {
146
- .filter-button {
147
- padding: 0.75rem 1rem;
148
- font-size: 1.2rem;
149
- }
147
+ .filter-button {
148
+ padding: 0.75rem 1rem;
149
+ font-size: 1.2rem;
150
+ }
150
151
  }
151
152
 
152
153
  .filter-button-active {
153
- background-color: var(--grav-crud-color-neutral);
154
- color: var(--grav-crud-color-bg);
154
+ background-color: var(--grav-crud-color-neutral);
155
+ color: var(--grav-crud-color-bg);
156
+ }
157
+
158
+ .filter-apply-button {
159
+ display: inline-flex;
160
+ align-items: center;
161
+ justify-content: center;
162
+ cursor: pointer;
163
+ height: 2.5rem;
164
+ font-family: var(--grav-crud-button-font-family, "mundial", sans-serif);
165
+ font-size: 1.1rem;
166
+ font-weight: var(--grav-crud-button-font-weight, 500);
167
+ line-height: var(--grav-crud-button-line-height, 1.5);
168
+ border-width: var(--grav-crud-filter-border-width, 1.5px);
169
+ border-style: solid;
170
+ border-radius: var(--grav-crud-filter-border-radius, 0.5rem);
171
+ padding: 0.75rem 1rem;
172
+ background-color: #6b7280;
173
+ color: white;
174
+ border-color: #6b7280;
175
+ }
176
+
177
+ .filter-apply-button:hover {
178
+ background-color: #4b5563;
179
+ border-color: #4b5563;
155
180
  }
156
181
 
157
- .filters-actions {
158
- display: flex;
159
- justify-content: right;
160
- align-items: center;
182
+ @media (min-width: 640px) {
183
+ .filter-apply-button {
184
+ padding: 0.75rem 1rem;
185
+ font-size: 1.2rem;
186
+ }
187
+ }
188
+
189
+ /* Botones de acción de filtros (Limpiar, Filtrar) - alineados a la derecha */
190
+ .crud-filters-container > .filters-actions {
191
+ justify-content: flex-end;
192
+ margin-top: 0.5rem;
161
193
  }
162
194
 
163
195
  .filter-button-middle {
164
- border-radius: 0;
196
+ border-radius: 0;
165
197
  }
166
198
 
167
199
  .filter-button-right {
168
- border-radius: 0 var(--grav-crud-filter-border-radius, 0.5rem) var(--grav-crud-filter-border-radius, 0.5rem) 0;
200
+ border-radius: 0 var(--grav-crud-filter-border-radius, 0.5rem)
201
+ var(--grav-crud-filter-border-radius, 0.5rem) 0;
169
202
  }
170
203
 
171
204
  .page-size-input {
172
- width: 8rem;
205
+ width: 8rem;
173
206
  }
174
207
 
175
208
  @media (min-width: 640px) {
176
- .page-size-input {
177
- width: 4rem;
178
- }
209
+ .page-size-input {
210
+ width: 4rem;
211
+ }
179
212
  }
180
213
 
181
214
  .apply-filter-button {
182
- display: inline-flex;
183
- height: 3rem;
184
- cursor: pointer;
185
- align-items: center;
186
- padding: 0.75rem 1rem;
187
- font-family: var(--grav-crud-button-font-family, 'mundial', sans-serif);
188
- font-size: var(--grav-crud-button-font-size, 0.875rem);
189
- font-weight: var(--grav-crud-button-font-weight, 500);
190
- line-height: var(--grav-crud-button-line-height, 1.5);
191
- border-width: var(--grav-crud-filter-border-width, 1.5px);
192
- border-style: solid;
193
- border-color: var(--grav-crud-color-neutral);
194
- color: var(--grav-crud-color-neutral);
195
- border-radius: var(--grav-crud-filter-border-radius, 0.5rem);
215
+ display: inline-flex;
216
+ height: 3rem;
217
+ cursor: pointer;
218
+ align-items: center;
219
+ padding: 0.75rem 1rem;
220
+ font-family: var(--grav-crud-button-font-family, "mundial", sans-serif);
221
+ font-size: var(--grav-crud-button-font-size, 0.875rem);
222
+ font-weight: var(--grav-crud-button-font-weight, 500);
223
+ line-height: var(--grav-crud-button-line-height, 1.5);
224
+ border-width: var(--grav-crud-filter-border-width, 1.5px);
225
+ border-style: solid;
226
+ border-color: var(--grav-crud-color-neutral);
227
+ color: var(--grav-crud-color-neutral);
228
+ border-radius: var(--grav-crud-filter-border-radius, 0.5rem);
196
229
  }
197
230
 
198
231
  @media (min-width: 640px) {
199
- .apply-filter-button {
200
- padding: 0.75rem 1rem;
201
- font-size: 0.875rem;
202
- }
232
+ .apply-filter-button {
233
+ padding: 0.75rem 1rem;
234
+ font-size: 0.875rem;
235
+ }
203
236
  }
204
237
 
205
238
  .filters-grid {
206
- display: grid;
207
- grid-template-columns: repeat(1, 1fr);
208
- gap: 0.2rem;
239
+ display: grid;
240
+ grid-template-columns: repeat(1, 1fr);
241
+ gap: 0.2rem;
209
242
  }
210
243
 
211
244
  @media (min-width: 640px) {
212
- .filters-grid {
213
- grid-template-columns: repeat(var(--grid-columns), 1fr);
214
- gap: 0.8rem;
215
- }
245
+ .filters-grid {
246
+ grid-template-columns: repeat(var(--grid-columns), 1fr);
247
+ gap: 0.8rem;
248
+ }
216
249
  }
217
250
 
218
251
  .filter-item {
219
- position: relative;
220
- margin: auto 0;
252
+ position: relative;
253
+ margin: auto 0;
221
254
  }
222
255
 
223
256
  .filter-item-number {
224
- z-index: 50;
257
+ z-index: 50;
225
258
  }
226
259
 
227
260
  .filter-item-datetime {
228
- z-index: 50;
261
+ z-index: 50;
229
262
  }
230
263
 
231
264
  .filter-item-date {
232
- z-index: 50;
265
+ z-index: 50;
233
266
  }
234
267
 
235
268
  .filter-item-bool {
236
- margin: auto 0;
269
+ margin: auto 0;
237
270
  }
238
271
 
239
272
  .loading-spinner {
240
- display: flex;
241
- justify-content: center;
242
- align-items: center;
243
- min-height: 38px;
273
+ display: flex;
274
+ justify-content: center;
275
+ align-items: center;
276
+ min-height: 38px;
244
277
  }
245
278
 
246
279
  .spinner {
247
- width: 20px;
248
- height: 20px;
249
- border: 2px solid #f3f3f3;
250
- border-top: 2px solid #3498db;
251
- border-radius: 50%;
252
- animation: spin 1s linear infinite;
280
+ width: 20px;
281
+ height: 20px;
282
+ border: 2px solid #f3f3f3;
283
+ border-top: 2px solid #3498db;
284
+ border-radius: 50%;
285
+ animation: spin 1s linear infinite;
253
286
  }
254
287
 
255
288
  @keyframes spin {
256
- 0% { transform: rotate(0deg); }
257
- 100% { transform: rotate(360deg); }
258
- }
289
+ 0% {
290
+ transform: rotate(0deg);
291
+ }
292
+ 100% {
293
+ transform: rotate(360deg);
294
+ }
295
+ }
@@ -310,7 +310,7 @@
310
310
  <button
311
311
  type="button"
312
312
  on:click={() => actualizarFiltro()}
313
- class="filter-button"
313
+ class="filter-button filter-apply-button"
314
314
  aria-label="Aplicar filtros"
315
315
  >
316
316
  Filtrar
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { ButtonConfig } from "./interfaces.js";
3
3
  import Tooltip from "./Tooltip.svelte";
4
+ import { onMount, afterUpdate } from "svelte";
4
5
 
5
6
  export let id = 1;
6
7
  export let buttonsConfig: ButtonConfig[];
@@ -8,14 +9,61 @@
8
9
  export let row: any = undefined;
9
10
 
10
11
  $: visibleButtons = buttonsConfig.filter((btn) => btn.show ?? true);
12
+
13
+ function handleClick(event: MouseEvent, button: ButtonConfig) {
14
+ event.stopPropagation();
15
+ button.action(id, row);
16
+ }
17
+
18
+ // Prevenir que Font Awesome procese iconos múltiples veces
19
+ function preventIconDuplication(element: HTMLElement) {
20
+ if (!element) return;
21
+
22
+ const buttons = element.querySelectorAll('button');
23
+ buttons.forEach((button) => {
24
+ const icons = button.querySelectorAll('i[class*="fa-"]');
25
+ // Si hay más de un icono, eliminar los duplicados (mantener solo el primero)
26
+ if (icons.length > 1) {
27
+ for (let i = 1; i < icons.length; i++) {
28
+ icons[i].remove();
29
+ }
30
+ }
31
+ // Marcar como procesado para evitar que Font Awesome lo procese de nuevo
32
+ icons.forEach((icon) => {
33
+ if (!icon.hasAttribute('data-fa-processed')) {
34
+ icon.setAttribute('data-fa-processed', 'true');
35
+ }
36
+ // Prevenir que Font Awesome convierta a SVG si ya es SVG
37
+ const svg = icon.querySelector('svg');
38
+ if (svg && icon.parentElement) {
39
+ // Si ya hay un SVG, eliminar el icono original
40
+ icon.style.display = 'none';
41
+ }
42
+ });
43
+ });
44
+ }
45
+
46
+ let buttonGroupElement: HTMLDivElement;
47
+
48
+ onMount(() => {
49
+ if (buttonGroupElement) {
50
+ preventIconDuplication(buttonGroupElement);
51
+ }
52
+ });
53
+
54
+ afterUpdate(() => {
55
+ if (buttonGroupElement) {
56
+ preventIconDuplication(buttonGroupElement);
57
+ }
58
+ });
11
59
  </script>
12
60
 
13
- <div class="button-group" role="group">
14
- {#each visibleButtons as button, i}
61
+ <div class="button-group" role="group" bind:this={buttonGroupElement}>
62
+ {#each visibleButtons as button, i (button.icon + id + i)}
15
63
  <Tooltip text={button.tooltip}>
16
64
  <button
17
65
  aria-label={button.tooltip}
18
- on:click={() => button.action(id, row)}
66
+ on:click={(e) => handleClick(e, button)}
19
67
  type="button"
20
68
  class="action-buttons-group {visibleButtons.length === 1
21
69
  ? 'rounded-left rounded-right'
@@ -25,7 +73,7 @@
25
73
  ? 'rounded-right'
26
74
  : ''} {button.color}"
27
75
  >
28
- <i class={button.icon}> </i>
76
+ <i class={button.icon} data-fa-processed="true" data-fa-i2svg-processed="true"> </i>
29
77
  </button>
30
78
  </Tooltip>
31
79
  {/each}
@@ -214,6 +214,9 @@
214
214
  }
215
215
 
216
216
  .export-button {
217
+ display: inline-flex;
218
+ align-items: center;
219
+ justify-content: center;
217
220
  text-align: center;
218
221
  cursor: pointer;
219
222
  color: white;
@@ -8,6 +8,9 @@
8
8
  }
9
9
 
10
10
  .pagination-button {
11
+ display: inline-flex;
12
+ align-items: center;
13
+ justify-content: center;
11
14
  cursor: pointer;
12
15
  padding: 0.5rem;
13
16
  border-radius: var(--grav-crud-button-border-radius, 0.5rem);
@@ -3,6 +3,16 @@
3
3
 
4
4
  let showTooltip = false;
5
5
  export let text: string;
6
+
7
+ function handleMouseEnter(event: MouseEvent) {
8
+ event.stopPropagation();
9
+ showTooltip = true;
10
+ }
11
+
12
+ function handleMouseLeave(event: MouseEvent) {
13
+ event.stopPropagation();
14
+ showTooltip = false;
15
+ }
6
16
  </script>
7
17
 
8
18
  <div class="tooltip-container">
@@ -12,8 +22,8 @@
12
22
  </div>
13
23
  {/if}
14
24
  <div
15
- on:mouseenter={() => (showTooltip = true)}
16
- on:mouseleave={() => (showTooltip = false)}
25
+ on:mouseenter={handleMouseEnter}
26
+ on:mouseleave={handleMouseLeave}
17
27
  role="button"
18
28
  tabindex="0"
19
29
  >
@@ -7,6 +7,13 @@
7
7
 
8
8
  $: alignStyle = header.align === 'left' ? 'margin-right: auto;' :
9
9
  header.align === 'right' ? 'margin-left: auto;' : '';
10
+
11
+ function handleClick(event: MouseEvent) {
12
+ event.stopPropagation();
13
+ if (header.onButtonClick) {
14
+ header.onButtonClick(item[idField], item);
15
+ }
16
+ }
10
17
  </script>
11
18
 
12
19
  <div style="display: inline-flex; {alignStyle}">
@@ -14,15 +21,11 @@
14
21
  type="button"
15
22
  class="dynamic-button"
16
23
  style="{header.colorField && item[header.colorField] ? `background-color: ${item[header.colorField]}; color: white;` : ''} {header.styleField ? (item[header.styleField] ?? '') : ''}"
17
- on:click={() => {
18
- if (header.onButtonClick) {
19
- header.onButtonClick(item[idField], item);
20
- }
21
- }}
24
+ on:click={handleClick}
22
25
  >
23
26
  {#if header.iconField && item[header.iconField]}
24
27
  {#if !header.iconPosition || header.iconPosition === "left"}
25
- <i class="{item[header.iconField]} dynamic-button-icon-left"></i>
28
+ <i class="{item[header.iconField]} dynamic-button-icon-left" data-fa-processed="true" data-fa-i2svg-processed="true"></i>
26
29
  {/if}
27
30
  {/if}
28
31
  {#if header.textField && item[header.textField]}
@@ -30,7 +33,7 @@
30
33
  {/if}
31
34
  {#if header.iconField && item[header.iconField]}
32
35
  {#if header.iconPosition === "right"}
33
- <i class="{item[header.iconField]} dynamic-button-icon-right"></i>
36
+ <i class="{item[header.iconField]} dynamic-button-icon-right" data-fa-processed="true" data-fa-i2svg-processed="true"></i>
34
37
  {/if}
35
38
  {/if}
36
39
  </button>