@djcali570/component-lib 0.1.91 → 0.1.93

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.
@@ -95,6 +95,8 @@
95
95
  display: flex;
96
96
  align-items: center;
97
97
  cursor: pointer;
98
+ /* REMOVE mobile tap highlight */
99
+ -webkit-tap-highlight-color: transparent;
98
100
  }
99
101
 
100
102
  .air5__checkbox__custom {
@@ -0,0 +1,240 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import { fly } from 'svelte/transition';
4
+ import type { Snippet } from 'svelte';
5
+ import type { ContextMenu5ColorScheme } from './types.js';
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
+ }
19
+
20
+ // Props
21
+ let {
22
+ icon,
23
+ content,
24
+ colorScheme: partialColorScheme = {},
25
+ ariaLabel = 'Open context menu',
26
+ menuAlign = 'right',
27
+ open = $bindable()
28
+ }: {
29
+ icon?: Snippet;
30
+ content: Snippet;
31
+ colorScheme?: Partial<ContextMenu5ColorScheme>;
32
+ ariaLabel?: string;
33
+ menuAlign?: 'left' | 'right';
34
+ open?: boolean;
35
+ } = $props();
36
+
37
+ // Default colors
38
+ const defaultColorScheme: ContextMenu5ColorScheme = {
39
+ contentBgColor: '#121212',
40
+ iconFillColor: '#121212',
41
+ iconBorderColor: '#121212',
42
+ iconColor: '#F5F5F5'
43
+ };
44
+
45
+ const colorScheme = $derived({
46
+ ...defaultColorScheme,
47
+ ...partialColorScheme
48
+ });
49
+
50
+ // DOM refs
51
+ let triggerEl: HTMLElement | null = null;
52
+ let menuEl: HTMLElement | null = $state(null);
53
+
54
+ // menu position
55
+ let position = $state({
56
+ x: 0,
57
+ y: 0,
58
+ width: 0
59
+ });
60
+
61
+ function updatePosition() {
62
+ if (!triggerEl) return;
63
+
64
+ const rect = triggerEl.getBoundingClientRect();
65
+
66
+ position = {
67
+ x: rect.left,
68
+ y: rect.bottom,
69
+ width: rect.width
70
+ };
71
+ }
72
+
73
+ // Toggle menu
74
+ function toggle() {
75
+ open = !open;
76
+
77
+ if (open) {
78
+ updatePosition();
79
+ }
80
+ }
81
+
82
+ // Close menu
83
+ function closeMenu() {
84
+ open = false;
85
+ }
86
+
87
+ // Click outside
88
+ function handlePointerDown(e: PointerEvent) {
89
+ if (!open) return;
90
+
91
+ const target = e.target as Node;
92
+
93
+ if (menuEl && triggerEl && !menuEl.contains(target) && !triggerEl.contains(target)) {
94
+ closeMenu();
95
+ }
96
+ }
97
+
98
+ // Escape key
99
+ function handleKeyDown(e: KeyboardEvent) {
100
+ if (e.key === 'Escape') closeMenu();
101
+ }
102
+
103
+ onMount(() => {
104
+ document.addEventListener('pointerdown', handlePointerDown);
105
+ document.addEventListener('keydown', handleKeyDown);
106
+
107
+ window.addEventListener('scroll', updatePosition, true);
108
+ window.addEventListener('resize', updatePosition);
109
+
110
+ return () => {
111
+ document.removeEventListener('pointerdown', handlePointerDown);
112
+ document.removeEventListener('keydown', handleKeyDown);
113
+
114
+ window.removeEventListener('scroll', updatePosition, true);
115
+ window.removeEventListener('resize', updatePosition);
116
+ };
117
+ });
118
+ </script>
119
+
120
+ <!-- Trigger -->
121
+ <div
122
+ class="context5"
123
+ style="
124
+ --context5__IconBorderColor: {colorScheme.iconBorderColor};
125
+ --context5__IconColor: {colorScheme.iconColor};
126
+ --context5__IconFillColor: {colorScheme.iconFillColor};
127
+ "
128
+ >
129
+ <button
130
+ aria-label={ariaLabel}
131
+ type="button"
132
+ aria-haspopup="menu"
133
+ aria-expanded={open}
134
+ bind:this={triggerEl}
135
+ onclick={toggle}
136
+ class="context5__btn"
137
+ >
138
+ {#if icon}
139
+ <div class="context5__icon">
140
+ {@render icon()}
141
+ </div>
142
+ {:else}
143
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
144
+ <circle cx="12" cy="5" r="2" />
145
+ <circle cx="12" cy="12" r="2" />
146
+ <circle cx="12" cy="19" r="2" />
147
+ </svg>
148
+ {/if}
149
+ </button>
150
+ </div>
151
+
152
+ <!-- Portal Menu -->
153
+ {#if open}
154
+ <div
155
+ use:portal
156
+ bind:this={menuEl}
157
+ role="menu"
158
+ class="context5__menu context5__portal"
159
+ style="
160
+ left:{menuAlign === 'right' ? position.x + position.width : position.x}px;
161
+ top:{position.y}px;
162
+ --context5__ContentBgColor: {colorScheme.contentBgColor};
163
+ --context5__ContentBorderColor: {colorScheme.contentBorderColor};
164
+ "
165
+ class:right={menuAlign === 'right'}
166
+ class:left={menuAlign === 'left'}
167
+ transition:fly={{ y: 8 }}
168
+ >
169
+ <div class="context5__content">
170
+ {@render content()}
171
+ </div>
172
+ </div>
173
+ {/if}
174
+
175
+ <style>
176
+ .context5 {
177
+ position: relative;
178
+ display: inline-block;
179
+ }
180
+
181
+ .context5__icon {
182
+ width: 20px;
183
+ height: 20px;
184
+ display: flex;
185
+ align-items: center;
186
+ justify-content: center;
187
+ flex-shrink: 0;
188
+ }
189
+
190
+ .context5__icon :global(*) {
191
+ width: 100% !important;
192
+ height: 100% !important;
193
+ display: block;
194
+ }
195
+
196
+ .context5__btn {
197
+ width: 30px;
198
+ height: 30px;
199
+ padding: 0;
200
+ border-radius: 50%;
201
+ cursor: pointer;
202
+ background-color: var(--context5__IconFillColor);
203
+ border: 1px solid var(--context5__IconBorderColor);
204
+ display: flex;
205
+ align-items: center;
206
+ justify-content: center;
207
+ outline: none;
208
+ -webkit-tap-highlight-color: transparent;
209
+ }
210
+
211
+ .context5__btn > svg {
212
+ color: var(--context5__IconColor);
213
+ }
214
+
215
+ .context5__portal {
216
+ position: fixed;
217
+ z-index: 9999;
218
+ }
219
+
220
+ .context5__menu {
221
+ margin-top: 0.25rem;
222
+ transform-origin: top;
223
+ }
224
+
225
+ .context5__menu.right {
226
+ transform: translateX(-100%);
227
+ transform-origin: top right;
228
+ }
229
+
230
+ .context5__menu.left {
231
+ transform-origin: top left;
232
+ }
233
+
234
+ .context5__content {
235
+ background-color: var(--context5__ContentBgColor);
236
+ border-radius: 0.5rem;
237
+ padding: 0.5rem;
238
+ border: 1px solid var(--context5__ContentBorderColor);
239
+ }
240
+ </style>
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ContextMenu5ColorScheme } from './types.js';
3
+ type $$ComponentProps = {
4
+ icon?: Snippet;
5
+ content: Snippet;
6
+ colorScheme?: Partial<ContextMenu5ColorScheme>;
7
+ ariaLabel?: string;
8
+ menuAlign?: 'left' | 'right';
9
+ open?: boolean;
10
+ };
11
+ declare const ContextMenu5: import("svelte").Component<$$ComponentProps, {}, "open">;
12
+ type ContextMenu5 = ReturnType<typeof ContextMenu5>;
13
+ export default ContextMenu5;
@@ -1,12 +1,8 @@
1
1
  <script lang="ts">
2
- import { browser } from '$app/environment';
2
+ import { BROWSER } from 'esm-env';
3
3
  import { fly } from 'svelte/transition';
4
4
  import type { DatePicker5ColorScheme } from './types.js';
5
5
 
6
- /**
7
- * Date Picker 5 v0.0.1
8
- */
9
-
10
6
  let {
11
7
  colorScheme: partialColorScheme = {},
12
8
  title = '',
@@ -123,7 +119,7 @@
123
119
 
124
120
  // Close dropdown when clicking outside
125
121
  $effect(() => {
126
- if (!browser || !showDropdown) return;
122
+ if (!BROWSER || !showDropdown) return;
127
123
 
128
124
  const handleClickOutside = (event: MouseEvent) => {
129
125
  const target = event.target as Node;
@@ -149,7 +145,7 @@
149
145
  // Auto-scroll to make dropdown fully visible
150
146
 
151
147
  $effect(() => {
152
- if (!browser || !showDropdown || !dropdownRef) return;
148
+ if (!BROWSER || !showDropdown || !dropdownRef) return;
153
149
  setTimeout(() => {
154
150
  if (!dropdownRef) return;
155
151
  const dropdownRect = dropdownRef.getBoundingClientRect();
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { browser } from '$app/environment';
2
+ import { BROWSER } from 'esm-env';
3
3
  import { onDestroy, onMount } from 'svelte';
4
4
  import { fly } from 'svelte/transition';
5
5
  import type { DropDown5ColorScheme, DropDownItem } from './types.js';
@@ -47,7 +47,7 @@
47
47
  let filteredItems: DropDownItem[] = $state([]);
48
48
 
49
49
  $effect(() => {
50
- if (!browser || !showDropdown || !dropdownRef) return;
50
+ if (!BROWSER || !showDropdown || !dropdownRef) return;
51
51
  setTimeout(() => {
52
52
  if (!dropdownRef) return;
53
53
  const dropdownRect = dropdownRef.getBoundingClientRect();
package/dist/index.d.ts CHANGED
@@ -6,8 +6,9 @@ import Accordion5 from "./Accordion5.svelte";
6
6
  import Chart5 from "./Chart5.svelte";
7
7
  import AdminPanel5 from "./AdminPanel5.svelte";
8
8
  import Checkbox5 from "./Checkbox5.svelte";
9
- import type { Input5ColorScheme, DatePicker5ColorScheme, TimePicker5ColorScheme, DropDownItem, DropDown5ColorScheme, Accordion5ColorScheme, Chart5ColorScheme, AdminPanel5ColorScheme, BottomSheet5ColorScheme, Dialog5ColorScheme, Checkbox5ColorScheme } from "./types.js";
9
+ import ContextMenu5 from "./ContextMenu5.svelte";
10
+ import type { Input5ColorScheme, DatePicker5ColorScheme, TimePicker5ColorScheme, DropDownItem, DropDown5ColorScheme, Accordion5ColorScheme, Chart5ColorScheme, AdminPanel5ColorScheme, BottomSheet5ColorScheme, Dialog5ColorScheme, Checkbox5ColorScheme, ContextMenu5ColorScheme } from "./types.js";
10
11
  import BottomSheet5 from "./BottomSheet5.svelte";
11
12
  import Dialog5 from "./Dialog5.svelte";
12
- export { DatePicker5, Input5, DropDown5, TimePicker5, Accordion5, Chart5, AdminPanel5, BottomSheet5, Dialog5, Checkbox5 };
13
- export type { Input5ColorScheme, DatePicker5ColorScheme, TimePicker5ColorScheme, Accordion5ColorScheme, DropDown5ColorScheme, DropDownItem, Chart5ColorScheme, AdminPanel5ColorScheme, BottomSheet5ColorScheme, Dialog5ColorScheme, Checkbox5ColorScheme };
13
+ export { DatePicker5, Input5, DropDown5, TimePicker5, Accordion5, Chart5, AdminPanel5, BottomSheet5, Dialog5, Checkbox5, ContextMenu5 };
14
+ export type { Input5ColorScheme, DatePicker5ColorScheme, TimePicker5ColorScheme, Accordion5ColorScheme, DropDown5ColorScheme, DropDownItem, Chart5ColorScheme, AdminPanel5ColorScheme, BottomSheet5ColorScheme, Dialog5ColorScheme, Checkbox5ColorScheme, ContextMenu5ColorScheme };
package/dist/index.js CHANGED
@@ -9,4 +9,5 @@ import AdminPanel5 from "./AdminPanel5.svelte";
9
9
  import BottomSheet5 from "./BottomSheet5.svelte";
10
10
  import Dialog5 from "./Dialog5.svelte";
11
11
  import Checkbox5 from "./Checkbox5.svelte";
12
- export { DatePicker5, Input5, DropDown5, TimePicker5, Accordion5, Chart5, AdminPanel5, BottomSheet5, Dialog5, Checkbox5 };
12
+ import ContextMenu5 from "./ContextMenu5.svelte"
13
+ export { DatePicker5, Input5, DropDown5, TimePicker5, Accordion5, Chart5, AdminPanel5, BottomSheet5, Dialog5, Checkbox5, ContextMenu5 };
package/dist/types.d.ts CHANGED
@@ -98,3 +98,11 @@ export interface Checkbox5ColorScheme {
98
98
  focusedColor?: string;
99
99
  checkmarkColor?: string;
100
100
  }
101
+
102
+ export interface ContextMenu5ColorScheme {
103
+ contentBgColor?: string;
104
+ contentBorderColor?: string;
105
+ iconFillColor?: string;
106
+ iconBorderColor?: string;
107
+ iconColor?: string;
108
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djcali570/component-lib",
3
- "version": "0.1.91",
3
+ "version": "0.1.93",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",
@@ -19,6 +19,7 @@
19
19
  }
20
20
  },
21
21
  "peerDependencies": {
22
+ "@sveltejs/kit": "^2.50.2",
22
23
  "svelte": "^5.0.0"
23
24
  },
24
25
  "devDependencies": {
@@ -60,5 +61,8 @@
60
61
  "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
61
62
  "format": "prettier --write .",
62
63
  "lint": "prettier --check . && eslint ."
64
+ },
65
+ "dependencies": {
66
+ "esm-env": "^1.2.2"
63
67
  }
64
68
  }