@r2digisolutions/ui 0.24.0 → 0.24.1
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/dist/components/container/DataTable/components/ContextMenu.svelte +38 -6
- package/dist/components/container/DataTable/components/ContextMenu.svelte.d.ts +1 -0
- package/dist/components/container/DataTable/utils/portal.d.ts +3 -0
- package/dist/components/container/DataTable/utils/portal.js +13 -0
- package/dist/components/container/DataTable/utils/position.d.ts +4 -0
- package/dist/components/container/DataTable/utils/position.js +11 -0
- package/package.json +14 -14
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { TContextMenuEntry } from '../core/types.js';
|
|
3
|
+
import { portal } from '../utils/portal.js';
|
|
4
|
+
import { clampToViewport } from '../utils/position.js';
|
|
3
5
|
|
|
4
6
|
type Props = {
|
|
5
7
|
items?: TContextMenuEntry[];
|
|
@@ -9,6 +11,8 @@
|
|
|
9
11
|
title?: string;
|
|
10
12
|
searchable?: boolean;
|
|
11
13
|
context?: any;
|
|
14
|
+
// por si algún día quieres desactivar el portal:
|
|
15
|
+
portalTarget?: HTMLElement | null;
|
|
12
16
|
};
|
|
13
17
|
let {
|
|
14
18
|
items = [],
|
|
@@ -17,7 +21,8 @@
|
|
|
17
21
|
open = $bindable(false),
|
|
18
22
|
title = '',
|
|
19
23
|
searchable = true,
|
|
20
|
-
context = null
|
|
24
|
+
context = null,
|
|
25
|
+
portalTarget = null
|
|
21
26
|
}: Props = $props();
|
|
22
27
|
|
|
23
28
|
let stack = $state<{ label: string; items: TContextMenuEntry[] }[]>([]);
|
|
@@ -61,7 +66,6 @@
|
|
|
61
66
|
const query = q.trim().toLowerCase();
|
|
62
67
|
let arr = query ? list.filter((it) => matches(it, query)) : list.slice();
|
|
63
68
|
|
|
64
|
-
// limpiar divisores (sin duplicados, ni al principio/fin)
|
|
65
69
|
const out: TContextMenuEntry[] = [];
|
|
66
70
|
let prevDiv = false;
|
|
67
71
|
for (const it of arr) {
|
|
@@ -100,20 +104,44 @@
|
|
|
100
104
|
document.addEventListener('keydown', handler);
|
|
101
105
|
return () => document.removeEventListener('keydown', handler);
|
|
102
106
|
});
|
|
107
|
+
|
|
108
|
+
// --- CLAMP dinámico al abrir o al cambiar x/y ---
|
|
109
|
+
let menuEl: HTMLDivElement | null = $state(null);
|
|
110
|
+
|
|
111
|
+
$effect(() => {
|
|
112
|
+
if (!open || !menuEl) return;
|
|
113
|
+
// siguiente frame para medir dimensiones reales
|
|
114
|
+
requestAnimationFrame(() => {
|
|
115
|
+
if (!menuEl) return;
|
|
116
|
+
const rect = menuEl.getBoundingClientRect();
|
|
117
|
+
const { x: nx, y: ny } = clampToViewport(x, y, rect.width, rect.height, 8);
|
|
118
|
+
// solo si cambian, re-ubica
|
|
119
|
+
if (nx !== x || ny !== y) {
|
|
120
|
+
x = nx;
|
|
121
|
+
y = ny;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
103
125
|
</script>
|
|
104
126
|
|
|
105
127
|
{#if open}
|
|
128
|
+
<!-- BACKDROP: va al body por el portal también -->
|
|
106
129
|
<div
|
|
130
|
+
use:portal={portalTarget}
|
|
107
131
|
role="dialog"
|
|
108
|
-
class="fixed inset-0 z-
|
|
132
|
+
class="fixed inset-0 z-[2147483646]"
|
|
109
133
|
onclick={() => close()}
|
|
110
134
|
oncontextmenu={(e) => e.preventDefault()}
|
|
111
135
|
aria-modal="true"
|
|
112
136
|
tabindex="0"
|
|
137
|
+
style="pointer-events:auto"
|
|
113
138
|
/>
|
|
114
139
|
|
|
140
|
+
<!-- MENU: fijado al viewport y portaleado al body -->
|
|
115
141
|
<div
|
|
116
|
-
|
|
142
|
+
bind:this={menuEl}
|
|
143
|
+
use:portal={portalTarget}
|
|
144
|
+
class="fixed z-[2147483647] w-72 rounded-2xl bg-white p-2 shadow-xl ring-1 ring-black/5 dark:bg-gray-900"
|
|
117
145
|
style={`left:${x}px; top:${y}px`}
|
|
118
146
|
oncontextmenu={(e) => e.preventDefault()}
|
|
119
147
|
>
|
|
@@ -133,8 +161,10 @@
|
|
|
133
161
|
stroke="currentColor"
|
|
134
162
|
stroke-width="2"
|
|
135
163
|
stroke-linecap="round"
|
|
136
|
-
stroke-linejoin="round"
|
|
164
|
+
stroke-linejoin="round"
|
|
137
165
|
>
|
|
166
|
+
<polyline points="15 18 9 12 15 6" />
|
|
167
|
+
</svg>
|
|
138
168
|
</button>
|
|
139
169
|
{/if}
|
|
140
170
|
<div class="min-w-0 flex-1 truncate px-1 text-xs font-medium opacity-70">
|
|
@@ -181,8 +211,10 @@
|
|
|
181
211
|
stroke="currentColor"
|
|
182
212
|
stroke-width="2"
|
|
183
213
|
stroke-linecap="round"
|
|
184
|
-
stroke-linejoin="round"
|
|
214
|
+
stroke-linejoin="round"
|
|
185
215
|
>
|
|
216
|
+
<polyline points="9 18 15 12 9 6" />
|
|
217
|
+
</svg>
|
|
186
218
|
{/if}
|
|
187
219
|
</span>
|
|
188
220
|
</button>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function portal(node, target = null) {
|
|
2
|
+
const tgt = target ?? document.body;
|
|
3
|
+
const placeholder = document.createComment('portal-placeholder');
|
|
4
|
+
node.parentNode?.insertBefore(placeholder, node);
|
|
5
|
+
tgt.appendChild(node);
|
|
6
|
+
return {
|
|
7
|
+
destroy() {
|
|
8
|
+
node.remove();
|
|
9
|
+
placeholder.parentNode?.insertBefore(node, placeholder);
|
|
10
|
+
placeholder.remove();
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function clampToViewport(x, y, menuW, menuH, padding = 8) {
|
|
2
|
+
const vw = document.documentElement.clientWidth;
|
|
3
|
+
const vh = document.documentElement.clientHeight;
|
|
4
|
+
let nx = x;
|
|
5
|
+
let ny = y;
|
|
6
|
+
if (nx + menuW + padding > vw)
|
|
7
|
+
nx = Math.max(padding, vw - menuW - padding);
|
|
8
|
+
if (ny + menuH + padding > vh)
|
|
9
|
+
ny = Math.max(padding, vh - menuH - padding);
|
|
10
|
+
return { x: nx, y: ny };
|
|
11
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@r2digisolutions/ui",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"packageManager": "bun@1.2.23",
|
|
6
6
|
"publishConfig": {
|
|
@@ -53,20 +53,20 @@
|
|
|
53
53
|
"@playwright/test": "^1.55.1",
|
|
54
54
|
"@storybook/addon-essentials": "^8.6.14",
|
|
55
55
|
"@storybook/addon-interactions": "^8.6.14",
|
|
56
|
-
"@storybook/addon-svelte-csf": "5.0.
|
|
56
|
+
"@storybook/addon-svelte-csf": "5.0.10",
|
|
57
57
|
"@storybook/blocks": "^8.6.14",
|
|
58
|
-
"@storybook/svelte": "^9.1.
|
|
59
|
-
"@storybook/sveltekit": "^9.1.
|
|
58
|
+
"@storybook/svelte": "^9.1.10",
|
|
59
|
+
"@storybook/sveltekit": "^9.1.10",
|
|
60
60
|
"@storybook/test": "^8.6.14",
|
|
61
|
-
"@sveltejs/adapter-static": "^3.0.
|
|
62
|
-
"@sveltejs/kit": "^2.
|
|
61
|
+
"@sveltejs/adapter-static": "^3.0.10",
|
|
62
|
+
"@sveltejs/kit": "^2.44.0",
|
|
63
63
|
"@sveltejs/package": "^2.5.4",
|
|
64
64
|
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
|
65
|
-
"@tailwindcss/postcss": "^4.1.
|
|
65
|
+
"@tailwindcss/postcss": "^4.1.14",
|
|
66
66
|
"@testing-library/svelte": "^5.2.8",
|
|
67
67
|
"@vitest/browser": "^3.2.4",
|
|
68
68
|
"changeset": "^0.2.6",
|
|
69
|
-
"eslint": "^9.
|
|
69
|
+
"eslint": "^9.37.0",
|
|
70
70
|
"eslint-config-prettier": "^10.1.8",
|
|
71
71
|
"eslint-plugin-svelte": "^3.12.4",
|
|
72
72
|
"globals": "^16.4.0",
|
|
@@ -75,14 +75,14 @@
|
|
|
75
75
|
"prettier": "^3.6.2",
|
|
76
76
|
"prettier-plugin-svelte": "^3.4.0",
|
|
77
77
|
"prettier-plugin-tailwindcss": "^0.6.14",
|
|
78
|
-
"publint": "^0.3.
|
|
79
|
-
"storybook": "^9.1.
|
|
80
|
-
"svelte": "^5.39.
|
|
78
|
+
"publint": "^0.3.14",
|
|
79
|
+
"storybook": "^9.1.10",
|
|
80
|
+
"svelte": "^5.39.9",
|
|
81
81
|
"svelte-check": "^4.3.2",
|
|
82
|
-
"tailwindcss": "^4.1.
|
|
83
|
-
"typescript": "^5.9.
|
|
82
|
+
"tailwindcss": "^4.1.14",
|
|
83
|
+
"typescript": "^5.9.3",
|
|
84
84
|
"typescript-eslint": "^8.45.0",
|
|
85
|
-
"vite": "^7.1.
|
|
85
|
+
"vite": "^7.1.9",
|
|
86
86
|
"vitest": "^3.2.4"
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|