@djcali570/component-lib 0.1.92 → 0.1.94
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/ContextMenu5.svelte
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { onMount } from 'svelte';
|
|
3
|
+
import { fly } from 'svelte/transition';
|
|
3
4
|
import type { Snippet } from 'svelte';
|
|
4
5
|
import type { ContextMenu5ColorScheme } from './types.js';
|
|
5
|
-
|
|
6
|
+
|
|
7
|
+
// portal action
|
|
8
|
+
function portal(node: HTMLElement, target: HTMLElement = document.body) {
|
|
9
|
+
target.appendChild(node);
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
destroy() {
|
|
13
|
+
if (node.parentNode) {
|
|
14
|
+
node.parentNode.removeChild(node);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
6
19
|
|
|
7
20
|
// Props
|
|
8
21
|
let {
|
|
@@ -11,7 +24,8 @@
|
|
|
11
24
|
colorScheme: partialColorScheme = {},
|
|
12
25
|
ariaLabel = 'Open context menu',
|
|
13
26
|
menuAlign = 'right',
|
|
14
|
-
open = $bindable()
|
|
27
|
+
open = $bindable(),
|
|
28
|
+
iconSize = '20px'
|
|
15
29
|
}: {
|
|
16
30
|
icon?: Snippet;
|
|
17
31
|
content: Snippet;
|
|
@@ -19,16 +33,17 @@
|
|
|
19
33
|
ariaLabel?: string;
|
|
20
34
|
menuAlign?: 'left' | 'right';
|
|
21
35
|
open?: boolean;
|
|
36
|
+
iconSize?: string;
|
|
22
37
|
} = $props();
|
|
23
38
|
|
|
24
39
|
// Default colors
|
|
25
40
|
const defaultColorScheme: ContextMenu5ColorScheme = {
|
|
26
41
|
contentBgColor: '#121212',
|
|
27
|
-
contentBorderColor: '#191919',
|
|
28
42
|
iconFillColor: '#121212',
|
|
29
43
|
iconBorderColor: '#121212',
|
|
30
44
|
iconColor: '#F5F5F5'
|
|
31
45
|
};
|
|
46
|
+
|
|
32
47
|
const colorScheme = $derived({
|
|
33
48
|
...defaultColorScheme,
|
|
34
49
|
...partialColorScheme
|
|
@@ -38,9 +53,32 @@
|
|
|
38
53
|
let triggerEl: HTMLElement | null = null;
|
|
39
54
|
let menuEl: HTMLElement | null = $state(null);
|
|
40
55
|
|
|
56
|
+
// menu position
|
|
57
|
+
let position = $state({
|
|
58
|
+
x: 0,
|
|
59
|
+
y: 0,
|
|
60
|
+
width: 0
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
function updatePosition() {
|
|
64
|
+
if (!triggerEl) return;
|
|
65
|
+
|
|
66
|
+
const rect = triggerEl.getBoundingClientRect();
|
|
67
|
+
|
|
68
|
+
position = {
|
|
69
|
+
x: rect.left,
|
|
70
|
+
y: rect.bottom,
|
|
71
|
+
width: rect.width
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
41
75
|
// Toggle menu
|
|
42
76
|
function toggle() {
|
|
43
77
|
open = !open;
|
|
78
|
+
|
|
79
|
+
if (open) {
|
|
80
|
+
updatePosition();
|
|
81
|
+
}
|
|
44
82
|
}
|
|
45
83
|
|
|
46
84
|
// Close menu
|
|
@@ -53,6 +91,7 @@
|
|
|
53
91
|
if (!open) return;
|
|
54
92
|
|
|
55
93
|
const target = e.target as Node;
|
|
94
|
+
|
|
56
95
|
if (menuEl && triggerEl && !menuEl.contains(target) && !triggerEl.contains(target)) {
|
|
57
96
|
closeMenu();
|
|
58
97
|
}
|
|
@@ -67,21 +106,27 @@
|
|
|
67
106
|
document.addEventListener('pointerdown', handlePointerDown);
|
|
68
107
|
document.addEventListener('keydown', handleKeyDown);
|
|
69
108
|
|
|
109
|
+
window.addEventListener('scroll', updatePosition, true);
|
|
110
|
+
window.addEventListener('resize', updatePosition);
|
|
111
|
+
|
|
70
112
|
return () => {
|
|
71
113
|
document.removeEventListener('pointerdown', handlePointerDown);
|
|
72
114
|
document.removeEventListener('keydown', handleKeyDown);
|
|
115
|
+
|
|
116
|
+
window.removeEventListener('scroll', updatePosition, true);
|
|
117
|
+
window.removeEventListener('resize', updatePosition);
|
|
73
118
|
};
|
|
74
119
|
});
|
|
75
120
|
</script>
|
|
76
121
|
|
|
122
|
+
<!-- Trigger -->
|
|
77
123
|
<div
|
|
78
124
|
class="context5"
|
|
79
125
|
style="
|
|
80
|
-
--context5__ContentBgColor: {colorScheme.contentBgColor};
|
|
81
|
-
--context5__ContentBorderColor: {colorScheme.contentBorderColor};
|
|
82
126
|
--context5__IconBorderColor: {colorScheme.iconBorderColor};
|
|
83
127
|
--context5__IconColor: {colorScheme.iconColor};
|
|
84
128
|
--context5__IconFillColor: {colorScheme.iconFillColor};
|
|
129
|
+
--context5__IconSize: {iconSize};
|
|
85
130
|
"
|
|
86
131
|
>
|
|
87
132
|
<button
|
|
@@ -105,22 +150,30 @@
|
|
|
105
150
|
</svg>
|
|
106
151
|
{/if}
|
|
107
152
|
</button>
|
|
153
|
+
</div>
|
|
108
154
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
155
|
+
<!-- Portal Menu -->
|
|
156
|
+
{#if open}
|
|
157
|
+
<div
|
|
158
|
+
use:portal
|
|
159
|
+
bind:this={menuEl}
|
|
160
|
+
role="menu"
|
|
161
|
+
class="context5__menu context5__portal"
|
|
162
|
+
style="
|
|
163
|
+
left:{menuAlign === 'right' ? position.x + position.width : position.x}px;
|
|
164
|
+
top:{position.y}px;
|
|
165
|
+
--context5__ContentBgColor: {colorScheme.contentBgColor};
|
|
166
|
+
--context5__ContentBorderColor: {colorScheme.contentBorderColor};
|
|
167
|
+
"
|
|
168
|
+
class:right={menuAlign === 'right'}
|
|
169
|
+
class:left={menuAlign === 'left'}
|
|
170
|
+
transition:fly={{ y: 8 }}
|
|
171
|
+
>
|
|
172
|
+
<div class="context5__content">
|
|
173
|
+
{@render content()}
|
|
121
174
|
</div>
|
|
122
|
-
|
|
123
|
-
|
|
175
|
+
</div>
|
|
176
|
+
{/if}
|
|
124
177
|
|
|
125
178
|
<style>
|
|
126
179
|
.context5 {
|
|
@@ -129,55 +182,48 @@
|
|
|
129
182
|
}
|
|
130
183
|
|
|
131
184
|
.context5__icon {
|
|
132
|
-
width:
|
|
133
|
-
height:
|
|
185
|
+
width: var(--context5__IconSize);
|
|
186
|
+
height: var(--context5__IconSize);
|
|
134
187
|
display: flex;
|
|
135
188
|
align-items: center;
|
|
136
189
|
justify-content: center;
|
|
137
190
|
flex-shrink: 0;
|
|
138
191
|
}
|
|
139
192
|
|
|
140
|
-
/* force ANY svg inside to obey the container */
|
|
141
|
-
.context5__icon :global(*) {
|
|
142
|
-
width: 100% !important;
|
|
143
|
-
height: 100% !important;
|
|
144
|
-
display: block;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
193
|
.context5__btn {
|
|
148
|
-
width:
|
|
149
|
-
height:
|
|
150
|
-
padding: 0;
|
|
151
|
-
border-radius: 50%; /* fully round */
|
|
194
|
+
width: var(--context5__IconSize);
|
|
195
|
+
height: var(--context5__IconSize);
|
|
196
|
+
padding: 0;
|
|
152
197
|
cursor: pointer;
|
|
153
|
-
background
|
|
198
|
+
background: none;
|
|
154
199
|
border: none;
|
|
155
|
-
|
|
156
|
-
-webkit-tap-highlight-color: transparent; /* removes blue flash on touch devices */
|
|
157
|
-
display: flex; /* center the icon inside */
|
|
200
|
+
display: flex;
|
|
158
201
|
align-items: center;
|
|
159
202
|
justify-content: center;
|
|
160
|
-
|
|
203
|
+
outline: none;
|
|
204
|
+
-webkit-tap-highlight-color: transparent;
|
|
161
205
|
}
|
|
162
206
|
|
|
163
207
|
.context5__btn > svg {
|
|
164
208
|
color: var(--context5__IconColor);
|
|
165
209
|
}
|
|
166
210
|
|
|
211
|
+
.context5__portal {
|
|
212
|
+
position: fixed;
|
|
213
|
+
z-index: 9999;
|
|
214
|
+
}
|
|
215
|
+
|
|
167
216
|
.context5__menu {
|
|
168
|
-
|
|
169
|
-
margin-top: 0.5rem;
|
|
170
|
-
z-index: 1000;
|
|
217
|
+
margin-top: 0.25rem;
|
|
171
218
|
transform-origin: top;
|
|
172
219
|
}
|
|
173
220
|
|
|
174
221
|
.context5__menu.right {
|
|
175
|
-
|
|
222
|
+
transform: translateX(-100%);
|
|
176
223
|
transform-origin: top right;
|
|
177
224
|
}
|
|
178
225
|
|
|
179
226
|
.context5__menu.left {
|
|
180
|
-
left: 0;
|
|
181
227
|
transform-origin: top left;
|
|
182
228
|
}
|
|
183
229
|
|
|
@@ -7,6 +7,7 @@ type $$ComponentProps = {
|
|
|
7
7
|
ariaLabel?: string;
|
|
8
8
|
menuAlign?: 'left' | 'right';
|
|
9
9
|
open?: boolean;
|
|
10
|
+
iconSize?: string;
|
|
10
11
|
};
|
|
11
12
|
declare const ContextMenu5: import("svelte").Component<$$ComponentProps, {}, "open">;
|
|
12
13
|
type ContextMenu5 = ReturnType<typeof ContextMenu5>;
|