@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.
@@ -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
- import { fly } from 'svelte/transition';
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
- {#if open}
110
- <div
111
- bind:this={menuEl}
112
- role="menu"
113
- class="context5__menu"
114
- class:left={menuAlign === 'left'}
115
- class:right={menuAlign === 'right'}
116
- transition:fly={{ y: 10 }}
117
- >
118
- <div class="context5__content">
119
- {@render content()}
120
- </div>
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
- {/if}
123
- </div>
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: 20px;
133
- height: 20px;
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: 30px; /* set width */
149
- height: 30px; /* set height (same as width for a perfect circle) */
150
- padding: 0; /* remove extra padding */
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-color: var(--context5__IconFillColor);
198
+ background: none;
154
199
  border: none;
155
- outline: none;
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
- border: 1px solid var(--context5__IconBorderColor);
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
- position: absolute;
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
- right: 0;
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>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djcali570/component-lib",
3
- "version": "0.1.92",
3
+ "version": "0.1.94",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",