@r2digisolutions/ui 0.34.6 → 0.34.8
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.
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import DataTableToolbar from './components/DataTableToolbar.svelte';
|
|
9
9
|
import DataTableFooter from './components/DataTableFooter.svelte';
|
|
10
10
|
import ContextMenu from './components/ContextMenu.svelte';
|
|
11
|
+
import { portal } from '../DataTable/utils/portal.js';
|
|
11
12
|
|
|
12
13
|
interface CellContext<T> {
|
|
13
14
|
row: T;
|
|
@@ -57,23 +58,23 @@
|
|
|
57
58
|
{} as Record<keyof T, { left?: number; right?: number }>
|
|
58
59
|
);
|
|
59
60
|
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
// ✅ el contenedor que scrollea (tu overflow-auto)
|
|
62
|
+
let scrollEl = $state<HTMLDivElement | null>(null);
|
|
63
|
+
|
|
64
|
+
// CONTEXT
|
|
63
65
|
let contextPopover = $state<HTMLDivElement | null>(null);
|
|
64
66
|
let contextRow = $state<T | null>(null);
|
|
65
67
|
|
|
66
|
-
//
|
|
68
|
+
// punto donde “quieres” abrirlo (coords de viewport)
|
|
67
69
|
let contextPos = $state<{ x: number; y: number }>({ x: 0, y: 0 });
|
|
68
70
|
|
|
69
|
-
// ✅
|
|
71
|
+
// ✅ left/top final (sin transform)
|
|
70
72
|
let contextRender = $state<{ left: number; top: number }>({ left: 0, top: 0 });
|
|
71
73
|
|
|
72
74
|
const CONTEXT_MARGIN = 12;
|
|
73
75
|
const CONTEXT_GAP = 8;
|
|
74
76
|
|
|
75
77
|
let openRows = $state<Set<string>>(new Set());
|
|
76
|
-
|
|
77
78
|
const contextOpen = $derived(contextRow !== null);
|
|
78
79
|
|
|
79
80
|
// GRID / STICKY
|
|
@@ -86,7 +87,6 @@
|
|
|
86
87
|
parts.push(`${w}px`);
|
|
87
88
|
});
|
|
88
89
|
|
|
89
|
-
// Columna de acciones
|
|
90
90
|
parts.push('64px');
|
|
91
91
|
gridTemplate = parts.join(' ');
|
|
92
92
|
|
|
@@ -98,9 +98,7 @@
|
|
|
98
98
|
let accLeft = controller.multiSelect ? 40 : 0;
|
|
99
99
|
controller.mainColumns.forEach((col) => {
|
|
100
100
|
const w = controller.getColumnWidth(col.id as keyof T);
|
|
101
|
-
if (col.sticky === 'left') {
|
|
102
|
-
offsets[col.id as keyof T] = { left: accLeft };
|
|
103
|
-
}
|
|
101
|
+
if (col.sticky === 'left') offsets[col.id as keyof T] = { left: accLeft };
|
|
104
102
|
accLeft += w;
|
|
105
103
|
});
|
|
106
104
|
stickyOffsets = offsets;
|
|
@@ -118,45 +116,35 @@
|
|
|
118
116
|
selectAllEl.indeterminate = controller.someVisibleSelected;
|
|
119
117
|
});
|
|
120
118
|
|
|
121
|
-
//
|
|
119
|
+
// Cerrar por click fuera
|
|
122
120
|
$effect(() => {
|
|
123
121
|
function handleDocumentClick(event: MouseEvent) {
|
|
124
122
|
if (!contextOpen) return;
|
|
125
123
|
const target = event.target as HTMLElement;
|
|
126
|
-
if (!target.closest('[data-context-host="true"]'))
|
|
127
|
-
closeContext();
|
|
128
|
-
}
|
|
124
|
+
if (!target.closest('[data-context-host="true"]')) closeContext();
|
|
129
125
|
}
|
|
130
|
-
if (contextOpen) document.addEventListener('
|
|
131
|
-
return () =>
|
|
132
|
-
document.removeEventListener('click', handleDocumentClick);
|
|
133
|
-
};
|
|
126
|
+
if (contextOpen) document.addEventListener('mousedown', handleDocumentClick, true);
|
|
127
|
+
return () => document.removeEventListener('mousedown', handleDocumentClick, true);
|
|
134
128
|
});
|
|
135
129
|
|
|
136
|
-
// ✅ Reposicionar
|
|
130
|
+
// ✅ Reposicionar mientras está abierto: window resize + scroll del contenedor REAL
|
|
137
131
|
$effect(() => {
|
|
138
132
|
if (!contextOpen) return;
|
|
139
133
|
|
|
140
|
-
const handler = () =>
|
|
141
|
-
// si se mueve el viewport o scroll, recalculamos
|
|
142
|
-
positionContext();
|
|
143
|
-
};
|
|
134
|
+
const handler = () => void positionContext();
|
|
144
135
|
|
|
145
136
|
window.addEventListener('resize', handler);
|
|
146
|
-
window.addEventListener('scroll', handler, { passive: true, capture: true });
|
|
147
137
|
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
window.visualViewport?.addEventListener('scroll', handler);
|
|
138
|
+
// 👇 ESTE ES el scroll que te estaba faltando
|
|
139
|
+
scrollEl?.addEventListener('scroll', handler, { passive: true });
|
|
151
140
|
|
|
152
141
|
return () => {
|
|
153
142
|
window.removeEventListener('resize', handler);
|
|
154
|
-
|
|
155
|
-
window.visualViewport?.removeEventListener('resize', handler);
|
|
156
|
-
window.visualViewport?.removeEventListener('scroll', handler);
|
|
143
|
+
scrollEl?.removeEventListener('scroll', handler as any);
|
|
157
144
|
};
|
|
158
145
|
});
|
|
159
146
|
|
|
147
|
+
// RESIZE columns
|
|
160
148
|
let resizingId: keyof T | null = null;
|
|
161
149
|
let startX = 0;
|
|
162
150
|
let startWidth = 0;
|
|
@@ -174,8 +162,7 @@
|
|
|
174
162
|
function onResizeMove(event: MouseEvent) {
|
|
175
163
|
if (!resizingId) return;
|
|
176
164
|
const dx = event.clientX - startX;
|
|
177
|
-
|
|
178
|
-
controller.resizeColumn(resizingId, width);
|
|
165
|
+
controller.resizeColumn(resizingId, startWidth + dx);
|
|
179
166
|
}
|
|
180
167
|
|
|
181
168
|
function onResizeUp() {
|
|
@@ -216,13 +203,12 @@
|
|
|
216
203
|
|
|
217
204
|
function handleToggleAll(e: Event) {
|
|
218
205
|
const input = e.currentTarget as HTMLInputElement;
|
|
219
|
-
|
|
220
|
-
if (checked) controller.selectAllCurrentPage();
|
|
206
|
+
if (input.checked) controller.selectAllCurrentPage();
|
|
221
207
|
else controller.unselectAllCurrentPage();
|
|
222
208
|
}
|
|
223
209
|
|
|
224
210
|
// =========================
|
|
225
|
-
// ✅
|
|
211
|
+
// ✅ POSICIONAMIENTO REAL (sin transform)
|
|
226
212
|
// =========================
|
|
227
213
|
function clamp(n: number, min: number, max: number) {
|
|
228
214
|
return Math.max(min, Math.min(max, n));
|
|
@@ -234,20 +220,8 @@
|
|
|
234
220
|
|
|
235
221
|
function getViewport() {
|
|
236
222
|
const vv = window.visualViewport;
|
|
237
|
-
if (vv) {
|
|
238
|
-
|
|
239
|
-
left: vv.offsetLeft,
|
|
240
|
-
top: vv.offsetTop,
|
|
241
|
-
width: vv.width,
|
|
242
|
-
height: vv.height
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
return {
|
|
246
|
-
left: 0,
|
|
247
|
-
top: 0,
|
|
248
|
-
width: window.innerWidth,
|
|
249
|
-
height: window.innerHeight
|
|
250
|
-
};
|
|
223
|
+
if (vv) return { left: vv.offsetLeft, top: vv.offsetTop, width: vv.width, height: vv.height };
|
|
224
|
+
return { left: 0, top: 0, width: window.innerWidth, height: window.innerHeight };
|
|
251
225
|
}
|
|
252
226
|
|
|
253
227
|
function computeContextPosition(preferred: { x: number; y: number }, pop: DOMRect) {
|
|
@@ -259,30 +233,30 @@
|
|
|
259
233
|
const maxLeft = vp.left + vp.width - CONTEXT_MARGIN - pop.width;
|
|
260
234
|
const maxTop = vp.top + vp.height - CONTEXT_MARGIN - pop.height;
|
|
261
235
|
|
|
262
|
-
// Si es demasiado alto, lo pegamos arriba (y el popover scrollea)
|
|
263
236
|
const tooTall = pop.height > vp.height - CONTEXT_MARGIN * 2;
|
|
264
237
|
|
|
265
|
-
//
|
|
238
|
+
// default: left + down
|
|
266
239
|
let left = preferred.x - pop.width;
|
|
267
240
|
let top = preferred.y + CONTEXT_GAP;
|
|
268
241
|
|
|
269
|
-
//
|
|
270
|
-
if (left < minLeft)
|
|
271
|
-
left = preferred.x + CONTEXT_GAP;
|
|
272
|
-
}
|
|
242
|
+
// flip horizontal
|
|
243
|
+
if (left < minLeft) left = preferred.x + CONTEXT_GAP;
|
|
273
244
|
|
|
274
|
-
|
|
275
|
-
|
|
245
|
+
if (tooTall) {
|
|
246
|
+
// si no cabe en altura, pegado arriba
|
|
247
|
+
top = minTop;
|
|
248
|
+
} else {
|
|
249
|
+
// flip vertical si se sale abajo
|
|
276
250
|
const bottomIfDown = top + pop.height;
|
|
277
251
|
const bottomLimit = vp.top + vp.height - CONTEXT_MARGIN;
|
|
278
252
|
if (bottomIfDown > bottomLimit) {
|
|
279
|
-
top = preferred.y - CONTEXT_GAP - pop.height;
|
|
253
|
+
top = preferred.y - CONTEXT_GAP - pop.height; // arriba
|
|
280
254
|
}
|
|
281
|
-
|
|
282
|
-
|
|
255
|
+
|
|
256
|
+
// si arriba se sale, pegado abajo
|
|
257
|
+
if (top < minTop) top = maxTop;
|
|
283
258
|
}
|
|
284
259
|
|
|
285
|
-
// Clamp final
|
|
286
260
|
left = clamp(left, minLeft, Math.max(minLeft, maxLeft));
|
|
287
261
|
top = clamp(top, minTop, Math.max(minTop, maxTop));
|
|
288
262
|
|
|
@@ -292,21 +266,18 @@
|
|
|
292
266
|
async function positionContext() {
|
|
293
267
|
if (!contextPopover || !contextRow) return;
|
|
294
268
|
|
|
295
|
-
// Asegura layout real (Popover API a veces “late layout”)
|
|
296
269
|
await raf();
|
|
270
|
+
const r1 = contextPopover.getBoundingClientRect();
|
|
271
|
+
if (!r1.width || !r1.height) return;
|
|
297
272
|
|
|
298
|
-
|
|
299
|
-
if (!rect1.width || !rect1.height) return;
|
|
300
|
-
|
|
301
|
-
// 1ª pasada
|
|
302
|
-
contextRender = computeContextPosition(contextPos, rect1);
|
|
273
|
+
contextRender = computeContextPosition(contextPos, r1);
|
|
303
274
|
|
|
304
|
-
//
|
|
275
|
+
// segunda pasada por scrollbar
|
|
305
276
|
await raf();
|
|
306
|
-
const
|
|
307
|
-
if (!
|
|
277
|
+
const r2 = contextPopover.getBoundingClientRect();
|
|
278
|
+
if (!r2.width || !r2.height) return;
|
|
308
279
|
|
|
309
|
-
contextRender = computeContextPosition(contextPos,
|
|
280
|
+
contextRender = computeContextPosition(contextPos, r2);
|
|
310
281
|
}
|
|
311
282
|
|
|
312
283
|
async function openContextAt(event: MouseEvent, row: T) {
|
|
@@ -316,9 +287,6 @@
|
|
|
316
287
|
contextRow = row;
|
|
317
288
|
contextPos = { x: event.clientX, y: event.clientY };
|
|
318
289
|
|
|
319
|
-
if (contextPopover) contextPopover.showPopover();
|
|
320
|
-
|
|
321
|
-
// Espera render + posiciona
|
|
322
290
|
await tick();
|
|
323
291
|
await positionContext();
|
|
324
292
|
}
|
|
@@ -330,23 +298,21 @@
|
|
|
330
298
|
const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();
|
|
331
299
|
contextRow = row;
|
|
332
300
|
|
|
333
|
-
//
|
|
301
|
+
// punto: esquina inferior derecha del botón
|
|
334
302
|
contextPos = { x: rect.right, y: rect.bottom };
|
|
335
303
|
|
|
336
|
-
if (contextPopover) contextPopover.showPopover();
|
|
337
|
-
|
|
338
304
|
await tick();
|
|
339
305
|
await positionContext();
|
|
340
306
|
}
|
|
341
307
|
|
|
342
308
|
function closeContext() {
|
|
343
|
-
if (contextPopover) contextPopover.hidePopover();
|
|
344
309
|
contextRow = null;
|
|
345
310
|
}
|
|
346
311
|
</script>
|
|
347
312
|
|
|
313
|
+
<!-- ✅ wrapper principal relativo (para z-index) -->
|
|
348
314
|
<div
|
|
349
|
-
class="flex flex-col overflow-hidden rounded-2xl border border-neutral-200/80 bg-neutral-50/70 text-xs text-neutral-900 shadow-sm backdrop-blur-2xl dark:border-neutral-800/80 dark:bg-neutral-950/70 dark:text-neutral-50"
|
|
315
|
+
class="relative flex flex-col overflow-hidden rounded-2xl border border-neutral-200/80 bg-neutral-50/70 text-xs text-neutral-900 shadow-sm backdrop-blur-2xl dark:border-neutral-800/80 dark:bg-neutral-950/70 dark:text-neutral-50"
|
|
350
316
|
>
|
|
351
317
|
<DataTableToolbar
|
|
352
318
|
{density}
|
|
@@ -366,7 +332,8 @@
|
|
|
366
332
|
</div>
|
|
367
333
|
{/if}
|
|
368
334
|
|
|
369
|
-
|
|
335
|
+
<!-- ✅ ESTE es el scroll container, le hacemos bind -->
|
|
336
|
+
<div bind:this={scrollEl} class="relative max-h-[70vh] flex-1 overflow-auto">
|
|
370
337
|
{#if controller.loading}
|
|
371
338
|
<div class="pointer-events-none absolute inset-0 z-20 bg-neutral-900/30 backdrop-blur-md">
|
|
372
339
|
<div class="flex h-full items-center justify-center">
|
|
@@ -476,8 +443,6 @@
|
|
|
476
443
|
{@const id = rowIdFor(row, index)}
|
|
477
444
|
|
|
478
445
|
<div class="group relative">
|
|
479
|
-
<!-- Fila principal -->
|
|
480
|
-
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
481
446
|
<div
|
|
482
447
|
role="row"
|
|
483
448
|
tabindex="0"
|
|
@@ -522,22 +487,9 @@
|
|
|
522
487
|
: ''}
|
|
523
488
|
>
|
|
524
489
|
{#if cell}
|
|
525
|
-
{@render cell({
|
|
526
|
-
row,
|
|
527
|
-
column: col,
|
|
528
|
-
value,
|
|
529
|
-
index
|
|
530
|
-
})}
|
|
490
|
+
{@render cell({ row, column: col, value, index })}
|
|
531
491
|
{:else}
|
|
532
|
-
<span
|
|
533
|
-
class={`line-clamp-2 text-black dark:text-neutral-50 ${
|
|
534
|
-
col.align === 'right'
|
|
535
|
-
? 'ml-auto text-right'
|
|
536
|
-
: col.align === 'center'
|
|
537
|
-
? 'mx-auto text-center'
|
|
538
|
-
: ''
|
|
539
|
-
}`}
|
|
540
|
-
>
|
|
492
|
+
<span class="line-clamp-2 text-black dark:text-neutral-50">
|
|
541
493
|
{formatValue(col, value, row)}
|
|
542
494
|
</span>
|
|
543
495
|
{/if}
|
|
@@ -551,33 +503,30 @@
|
|
|
551
503
|
data-stop-row-toggle="true"
|
|
552
504
|
>
|
|
553
505
|
{#if actions.length}
|
|
554
|
-
|
|
555
|
-
{
|
|
556
|
-
{:else}
|
|
557
|
-
<div class="flex items-center gap-1.5">
|
|
558
|
-
{#if rowCollapse}
|
|
559
|
-
<button
|
|
560
|
-
type="button"
|
|
561
|
-
onclick={(e) => {
|
|
562
|
-
e.stopPropagation();
|
|
563
|
-
toggleRow(row, index);
|
|
564
|
-
}}
|
|
565
|
-
class={`inline-flex h-6 w-6 items-center justify-center rounded-full text-neutral-400 transition-colors hover:bg-neutral-200/80 hover:text-neutral-800 dark:text-neutral-400 dark:hover:bg-neutral-800/80 dark:hover:text-neutral-100 ${
|
|
566
|
-
openRows.has(id) ? 'rotate-180' : ''
|
|
567
|
-
}`}
|
|
568
|
-
>
|
|
569
|
-
<ChevronDown class="h-3.5 w-3.5" />
|
|
570
|
-
</button>
|
|
571
|
-
{/if}
|
|
506
|
+
<div class="flex items-center gap-1.5">
|
|
507
|
+
{#if rowCollapse}
|
|
572
508
|
<button
|
|
573
509
|
type="button"
|
|
574
|
-
onclick={(e) =>
|
|
575
|
-
|
|
510
|
+
onclick={(e) => {
|
|
511
|
+
e.stopPropagation();
|
|
512
|
+
toggleRow(row, index);
|
|
513
|
+
}}
|
|
514
|
+
class={`inline-flex h-6 w-6 items-center justify-center rounded-full text-neutral-400 transition-colors hover:bg-neutral-200/80 hover:text-neutral-800 dark:text-neutral-400 dark:hover:bg-neutral-800/80 dark:hover:text-neutral-100 ${
|
|
515
|
+
openRows.has(id) ? 'rotate-180' : ''
|
|
516
|
+
}`}
|
|
576
517
|
>
|
|
577
|
-
<
|
|
518
|
+
<ChevronDown class="h-3.5 w-3.5" />
|
|
578
519
|
</button>
|
|
579
|
-
|
|
580
|
-
|
|
520
|
+
{/if}
|
|
521
|
+
|
|
522
|
+
<button
|
|
523
|
+
type="button"
|
|
524
|
+
onclick={(e) => openContextFromButton(e, row)}
|
|
525
|
+
class="inline-flex h-7 w-7 items-center justify-center rounded-full text-neutral-400 transition-colors hover:bg-neutral-200/80 hover:text-neutral-800 dark:text-neutral-400 dark:hover:bg-neutral-800/80 dark:hover:text-neutral-100"
|
|
526
|
+
>
|
|
527
|
+
<EllipsisVertical class="h-4 w-4" />
|
|
528
|
+
</button>
|
|
529
|
+
</div>
|
|
581
530
|
{/if}
|
|
582
531
|
</div>
|
|
583
532
|
</div>
|
|
@@ -621,27 +570,13 @@
|
|
|
621
570
|
{/each}
|
|
622
571
|
</div>
|
|
623
572
|
{:else}
|
|
624
|
-
<!-- GRID VIEW -->
|
|
625
573
|
<div class="grid gap-3 p-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
|
626
574
|
{#each controller.currentRows as row, index (rowIdFor(row, index))}
|
|
627
|
-
{@const id = rowIdFor(row, index)}
|
|
628
|
-
{@const cols = controller.mainColumns as any[]}
|
|
629
|
-
{@const firstCol = cols[0]}
|
|
630
|
-
{@const firstValue = firstCol
|
|
631
|
-
? firstCol.accessor
|
|
632
|
-
? firstCol.accessor(row)
|
|
633
|
-
: (row as any)[firstCol.id]
|
|
634
|
-
: null}
|
|
635
|
-
{@const restCols = cols.slice(1)}
|
|
636
575
|
<div
|
|
637
|
-
class=
|
|
638
|
-
controller.selectedIds.has(id)
|
|
639
|
-
? 'bg-purple-50/70 ring-1 ring-purple-400/70 dark:bg-purple-950/20'
|
|
640
|
-
: ''
|
|
641
|
-
}`}
|
|
576
|
+
class="group relative rounded-2xl border border-neutral-200/80 bg-white/80 p-3 text-[11px] text-neutral-800 shadow-sm ring-0 transition-all hover:border-purple-400/70 hover:shadow-md dark:border-neutral-800/80 dark:bg-neutral-900/80 dark:text-neutral-50"
|
|
642
577
|
oncontextmenu={(e) => openContextAt(e, row)}
|
|
643
578
|
>
|
|
644
|
-
<!--
|
|
579
|
+
<!-- tu grid content -->
|
|
645
580
|
</div>
|
|
646
581
|
{/each}
|
|
647
582
|
</div>
|
|
@@ -655,22 +590,22 @@
|
|
|
655
590
|
</div>
|
|
656
591
|
|
|
657
592
|
<DataTableFooter />
|
|
658
|
-
|
|
659
|
-
<!-- ✅ CONTEXT POPOVER (FIXED) -->
|
|
660
|
-
<div
|
|
661
|
-
bind:this={contextPopover}
|
|
662
|
-
popover="manual"
|
|
663
|
-
data-context-host="true"
|
|
664
|
-
class="z-[1300] max-h-[calc(100vh-24px)] max-w-xs min-w-[190px] overflow-auto rounded-2xl border border-neutral-200/80 bg-neutral-50/95 p-1.5 text-xs text-neutral-900 shadow-[0_18px_50px_rgba(15,23,42,0.45)] backdrop-blur-2xl dark:border-neutral-700/80 dark:bg-neutral-900/95 dark:text-neutral-50"
|
|
665
|
-
style={`position: fixed; left: ${contextRender.left}px; top: ${contextRender.top}px;`}
|
|
666
|
-
onbeforetoggle={(e) => {
|
|
667
|
-
if ((e as any).newState === 'closed') contextRow = null;
|
|
668
|
-
}}
|
|
669
|
-
>
|
|
670
|
-
{#if contextRow && actions.length}
|
|
671
|
-
<ContextMenu {actions} row={contextRow} onClose={closeContext} />
|
|
672
|
-
{:else}
|
|
673
|
-
<div class="flex flex-col gap-2">No hay acciones disponibles</div>
|
|
674
|
-
{/if}
|
|
675
|
-
</div>
|
|
676
593
|
</div>
|
|
594
|
+
<!-- ✅ HOST fuera del overflow-auto (ya no lo recorta) -->
|
|
595
|
+
{#if contextOpen}
|
|
596
|
+
{#key contextRow}
|
|
597
|
+
<div
|
|
598
|
+
bind:this={contextPopover}
|
|
599
|
+
use:portal
|
|
600
|
+
data-context-host="true"
|
|
601
|
+
class="pointer-events-auto fixed z-[999999] max-h-[calc(100vh-24px)] max-w-xs min-w-[190px] overflow-auto rounded-2xl border border-neutral-200/80 bg-neutral-50/95 p-1.5 text-xs text-neutral-900 shadow-[0_18px_50px_rgba(15,23,42,0.45)] backdrop-blur-2xl dark:border-neutral-700/80 dark:bg-neutral-900/95 dark:text-neutral-50"
|
|
602
|
+
style={`left:${contextRender.left}px; top:${contextRender.top}px;`}
|
|
603
|
+
>
|
|
604
|
+
{#if contextRow && actions.length}
|
|
605
|
+
<ContextMenu {actions} row={contextRow} onClose={closeContext} />
|
|
606
|
+
{:else}
|
|
607
|
+
<div class="flex flex-col gap-2">No hay acciones disponibles</div>
|
|
608
|
+
{/if}
|
|
609
|
+
</div>
|
|
610
|
+
{/key}
|
|
611
|
+
{/if}
|