@r2digisolutions/ui 0.24.5 → 0.24.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.
@@ -124,23 +124,19 @@
124
124
  ) {
125
125
  e.preventDefault();
126
126
  const columnIndex = columnId ? manager.state.visibleColumns.indexOf(columnId) : null;
127
- // Always set fresh coordinates and ensure menu is closed before reopening
128
- if (rightMenu.open) {
129
- rightMenu = { open: false, x: 0, y: 0 };
130
- setTimeout(() => {
131
- rightMenu = { open: true, x: e.clientX, y: e.clientY };
132
- }, 0);
133
- } else {
127
+ // Reset menu state and set new coordinates
128
+ rightMenu = { open: false, x: 0, y: 0 };
129
+ tick().then(() => {
134
130
  rightMenu = { open: true, x: e.clientX, y: e.clientY };
135
- }
136
- rightClickContext = {
137
- row,
138
- rowIndex,
139
- columnId,
140
- columnIndex,
141
- event: e,
142
- column: columnId ? manager.getColumn(columnId) : null
143
- } as TDataTableCellContext<T>;
131
+ rightClickContext = {
132
+ row,
133
+ rowIndex,
134
+ columnId,
135
+ columnIndex,
136
+ event: e,
137
+ column: columnId ? manager.getColumn(columnId) : null
138
+ } as TDataTableCellContext<T>;
139
+ });
144
140
  }
145
141
 
146
142
  function selectedRows(): T[] {
@@ -148,7 +144,10 @@
148
144
  return manager.state.items.filter((r) => ids.has(rowId(r)));
149
145
  }
150
146
 
151
- // Tracks
147
+ const selectedRowsItems = $derived.by(() => {
148
+ return selectedRows();
149
+ });
150
+
152
151
  function colTrack(cId: string, measuring: boolean) {
153
152
  if (measuring) return 'max-content';
154
153
  const c = manager.getColumn(cId);
@@ -291,7 +290,7 @@
291
290
  data-dt-cell="1"
292
291
  data-col-id={cid}
293
292
  data-row-index={i}
294
- class="px-3"
293
+ class="flex h-full w-full items-center px-3"
295
294
  onclick={() => onRowClick?.(row)}
296
295
  oncontextmenu={(e) => onCellContext(e, row, cid, i)}
297
296
  >
@@ -384,7 +383,7 @@
384
383
  x={rightMenu.x}
385
384
  y={rightMenu.y}
386
385
  context={rightClickContext}
387
- items={(actions?.(selectedRows(), rightClickContext) ?? []).map((a) => ({
386
+ items={(actions?.(selectedRowsItems, rightClickContext) ?? []).map((a) => ({
388
387
  ...a,
389
388
  onClick: a.onClick
390
389
  }))}
@@ -5,14 +5,19 @@
5
5
  function portal(node: HTMLElement, target: HTMLElement | null = null) {
6
6
  const tgt = target ?? document.body;
7
7
  const placeholder = document.createComment('portal-placeholder');
8
- if (!node.parentNode) return { destroy: () => {} };
9
- node.parentNode.insertBefore(placeholder, node);
8
+ if (node.parentNode) {
9
+ node.parentNode.insertBefore(placeholder, node);
10
+ }
11
+ // Remove any existing menu instances
12
+ const existingMenus = document.querySelectorAll('.context-menu-portal');
13
+ existingMenus.forEach((menu) => menu.remove());
14
+ // Add class for identification
15
+ node.classList.add('context-menu-portal');
10
16
  tgt.appendChild(node);
11
17
  return {
12
18
  destroy() {
13
19
  if (node.isConnected) node.remove();
14
20
  if (placeholder.isConnected && placeholder.parentNode) {
15
- placeholder.parentNode.insertBefore(node, placeholder);
16
21
  placeholder.remove();
17
22
  }
18
23
  }
@@ -41,6 +46,7 @@
41
46
  context?: any;
42
47
  portalTarget?: HTMLElement | null;
43
48
  };
49
+
44
50
  let {
45
51
  items = [],
46
52
  x = 0,
@@ -65,6 +71,12 @@
65
71
  q = '';
66
72
  x = 0;
67
73
  y = 0;
74
+ if (menuEl) {
75
+ menuEl.style.left = '0px';
76
+ menuEl.style.top = '0px';
77
+ menuEl.remove();
78
+ menuEl = null;
79
+ }
68
80
  }
69
81
 
70
82
  function hasChildren(it: TContextMenuEntry) {
@@ -170,40 +182,21 @@
170
182
  };
171
183
  });
172
184
 
173
- // Clamp de posición tras abrir o cambiar coords
185
+ // Aplicar posición tras abrir o cambiar coords
174
186
  $effect(() => {
175
187
  if (!open || !menuEl) return;
176
- requestAnimationFrame(() => {
177
- if (!menuEl) return;
178
- const rect = menuEl.getBoundingClientRect();
179
- const { x: nx, y: ny } = clampToViewport(x, y, rect.width, rect.height, 8);
180
- if (nx !== x || ny !== y) {
181
- x = nx;
182
- y = ny;
183
- }
184
- });
188
+ const rect = menuEl.getBoundingClientRect();
189
+ const { x: nx, y: ny } = clampToViewport(x, y, rect.width, rect.height, 8);
190
+ menuEl.style.left = `${nx}px`;
191
+ menuEl.style.top = `${ny}px`;
185
192
  });
186
193
  </script>
187
194
 
188
195
  {#if open}
189
- <!-- BACKDROP -->
190
- <div
191
- use:portal={portalTarget}
192
- role="dialog"
193
- class="fixed inset-0 z-[2147483646]"
194
- onclick={() => close()}
195
- oncontextmenu={(e) => e.preventDefault()}
196
- aria-modal="true"
197
- tabindex="0"
198
- style="pointer-events:auto"
199
- />
200
-
201
- <!-- MENÚ -->
202
196
  <div
203
197
  bind:this={menuEl}
204
198
  use:portal={portalTarget}
205
- class="fixed z-[2147483647] w-72 rounded-2xl bg-white p-2 shadow-xl ring-1 ring-black/5 dark:bg-gray-900"
206
- style={`left:${x}px; top:${y}px`}
199
+ class="context-menu-portal fixed z-[2147483647] w-72 rounded-2xl bg-white p-2 shadow-xl ring-1 ring-black/5 dark:bg-gray-900"
207
200
  oncontextmenu={(e) => e.preventDefault()}
208
201
  >
209
202
  <div class="flex items-center gap-1 px-1 py-1">
@@ -241,42 +234,46 @@
241
234
  <div
242
235
  class="max-h-72 overflow-auto rounded-xl border border-gray-200 p-1 dark:border-gray-800 dark:bg-gray-950"
243
236
  >
244
- {#each filtered as it}
245
- {#if it.kind === 'divider'}
246
- <div class="my-1 border-t border-gray-200 dark:border-gray-800"></div>
247
- {:else if it.kind === 'label'}
248
- <div class="px-3 py-1 text-[11px] tracking-wide uppercase opacity-60">{it.label}</div>
249
- {:else}
250
- <button
251
- class="flex w-full items-center justify-between gap-3 px-3 py-2 text-left text-sm hover:bg-gray-100 disabled:opacity-50 dark:hover:bg-gray-800"
252
- role="dialog"
253
- disabled={it.disabled}
254
- onclick={() => clickItem(it)}
255
- >
256
- <span class="truncate">{it.label}</span>
257
- <span class="flex items-center gap-2">
258
- {#if it.shortcut}
259
- <kbd class="rounded bg-gray-100 px-1 text-[10px] dark:bg-gray-800"
260
- >{it.shortcut}</kbd
261
- >
262
- {/if}
263
- {#if hasChildren(it)}
264
- <svg
265
- width="14"
266
- height="14"
267
- viewBox="0 0 24 24"
268
- class="opacity-60"
269
- fill="none"
270
- stroke="currentColor"
271
- stroke-width="2"
272
- stroke-linecap="round"
273
- stroke-linejoin="round"><polyline points="9 18 15 12 9 6" /></svg
274
- >
275
- {/if}
276
- </span>
277
- </button>
278
- {/if}
279
- {/each}
237
+ {#if filtered.length}
238
+ {#each filtered as it}
239
+ {#if it.kind === 'divider'}
240
+ <div class="my-1 border-t border-gray-200 dark:border-gray-800"></div>
241
+ {:else if it.kind === 'label'}
242
+ <div class="px-3 py-1 text-[11px] tracking-wide uppercase opacity-60">{it.label}</div>
243
+ {:else}
244
+ <button
245
+ class="flex w-full items-center justify-between gap-3 px-3 py-2 text-left text-sm hover:bg-gray-100 disabled:opacity-50 dark:hover:bg-gray-800"
246
+ role="dialog"
247
+ disabled={it.disabled}
248
+ onclick={() => clickItem(it)}
249
+ >
250
+ <span class="truncate">{it.label}</span>
251
+ <span class="flex items-center gap-2">
252
+ {#if it.shortcut}
253
+ <kbd class="rounded bg-gray-100 px-1 text-[10px] dark:bg-gray-800"
254
+ >{it.shortcut}</kbd
255
+ >
256
+ {/if}
257
+ {#if hasChildren(it)}
258
+ <svg
259
+ width="14"
260
+ height="14"
261
+ viewBox="0 0 24 24"
262
+ class="opacity-60"
263
+ fill="none"
264
+ stroke="currentColor"
265
+ stroke-width="2"
266
+ stroke-linecap="round"
267
+ stroke-linejoin="round"><polyline points="9 18 15 12 9 6" /></svg
268
+ >
269
+ {/if}
270
+ </span>
271
+ </button>
272
+ {/if}
273
+ {/each}
274
+ {:else}
275
+ <div class="my-1 border-t border-gray-200 dark:border-gray-800">No hay resultados</div>
276
+ {/if}
280
277
  </div>
281
278
  </div>
282
279
  {/if}
@@ -21,11 +21,6 @@ import Checkbox from './ui/Checkbox/Checkbox.svelte';
21
21
  import Badge from './ui/Badge/Badge.svelte';
22
22
  import NoContent from './ui/NoContent/NoContent.svelte';
23
23
  import Tag from './ui/Tag/Tag.svelte';
24
- import Dialog from './ui/Dialog/Dialog.svelte';
25
- import DialogHeader from './ui/Dialog/DialogHeader.svelte';
26
- import DialogTitle from './ui/Dialog/DialogTitle.svelte';
27
- import DialogDescription from './ui/Dialog/DialogDescription.svelte';
28
- import DialogContent from './ui/Dialog/DialogContent.svelte';
29
- import DialogFooter from './ui/Dialog/DialogFooter.svelte';
24
+ export * from './ui/Dialog/index.js';
30
25
  import Selector from './ui/Selector/Selector.svelte';
31
- export { Selector, Dialog, DialogHeader, DialogTitle, DialogDescription, DialogContent, DialogFooter, Tag, NoContent, Alert, Avatar, Button, Badge, Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, Container, Checkbox, Field, Section, Loading, TableList, Heading, Label, Input, InputRadio, Textarea, };
26
+ export { Selector, Tag, NoContent, Alert, Avatar, Button, Badge, Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, Container, Checkbox, Field, Section, Loading, TableList, Heading, Label, Input, InputRadio, Textarea, };
@@ -21,11 +21,6 @@ import Checkbox from './ui/Checkbox/Checkbox.svelte';
21
21
  import Badge from './ui/Badge/Badge.svelte';
22
22
  import NoContent from './ui/NoContent/NoContent.svelte';
23
23
  import Tag from './ui/Tag/Tag.svelte';
24
- import Dialog from './ui/Dialog/Dialog.svelte';
25
- import DialogHeader from './ui/Dialog/DialogHeader.svelte';
26
- import DialogTitle from './ui/Dialog/DialogTitle.svelte';
27
- import DialogDescription from './ui/Dialog/DialogDescription.svelte';
28
- import DialogContent from './ui/Dialog/DialogContent.svelte';
29
- import DialogFooter from './ui/Dialog/DialogFooter.svelte';
24
+ export * from './ui/Dialog/index.js';
30
25
  import Selector from './ui/Selector/Selector.svelte';
31
- export { Selector, Dialog, DialogHeader, DialogTitle, DialogDescription, DialogContent, DialogFooter, Tag, NoContent, Alert, Avatar, Button, Badge, Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, Container, Checkbox, Field, Section, Loading, TableList, Heading, Label, Input, InputRadio, Textarea, };
26
+ export { Selector, Tag, NoContent, Alert, Avatar, Button, Badge, Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, Container, Checkbox, Field, Section, Loading, TableList, Heading, Label, Input, InputRadio, Textarea, };
@@ -1,7 +1,7 @@
1
1
  import { ModalContext } from './ModalContext.svelte';
2
- export declare const KEY: unique symbol;
3
- export interface DialogActions {
2
+ export declare const KEY_DIALOG: unique symbol;
3
+ export interface IDialogActions {
4
4
  onOpenChange?(open: boolean): void;
5
5
  }
6
- export declare function createDialogContext(fn_open: any, actions?: DialogActions): ModalContext;
6
+ export declare function createDialogContext(fn_open: any, actions?: IDialogActions): ModalContext;
7
7
  export declare function useDialogContext(): ModalContext;
@@ -1,9 +1,9 @@
1
1
  import { getContext, setContext } from 'svelte';
2
2
  import { ModalContext } from './ModalContext.svelte';
3
- export const KEY = Symbol('Dialog');
3
+ export const KEY_DIALOG = Symbol('Dialog');
4
4
  export function createDialogContext(fn_open, actions = {}) {
5
5
  const context = new ModalContext(fn_open());
6
- setContext(KEY, context);
6
+ setContext(KEY_DIALOG, context);
7
7
  $effect(() => {
8
8
  context.open = fn_open();
9
9
  actions.onOpenChange?.(context.open);
@@ -15,5 +15,5 @@ export function createDialogContext(fn_open, actions = {}) {
15
15
  return context;
16
16
  }
17
17
  export function useDialogContext() {
18
- return getContext(KEY);
18
+ return getContext(KEY_DIALOG);
19
19
  }
@@ -0,0 +1,8 @@
1
+ import Dialog from './Dialog.svelte';
2
+ import DialogTitle from './DialogTitle.svelte';
3
+ import DialogDescription from './DialogDescription.svelte';
4
+ import DialogContent from './DialogContent.svelte';
5
+ import DialogHeader from './DialogHeader.svelte';
6
+ import DialogFooter from './DialogFooter.svelte';
7
+ export * from './context.svelte.js';
8
+ export { Dialog, DialogTitle, DialogDescription, DialogContent, DialogHeader, DialogFooter };
@@ -0,0 +1,8 @@
1
+ import Dialog from './Dialog.svelte';
2
+ import DialogTitle from './DialogTitle.svelte';
3
+ import DialogDescription from './DialogDescription.svelte';
4
+ import DialogContent from './DialogContent.svelte';
5
+ import DialogHeader from './DialogHeader.svelte';
6
+ import DialogFooter from './DialogFooter.svelte';
7
+ export * from './context.svelte.js';
8
+ export { Dialog, DialogTitle, DialogDescription, DialogContent, DialogHeader, DialogFooter };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@r2digisolutions/ui",
3
- "version": "0.24.5",
3
+ "version": "0.24.8",
4
4
  "private": false,
5
5
  "packageManager": "bun@1.2.23",
6
6
  "publishConfig": {