@djcali570/component-lib 0.1.96 → 0.1.98

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/README.md CHANGED
@@ -1,9 +1,14 @@
1
1
  # Svelte Component library
2
2
 
3
- 0.1.95
3
+ 0.1.98
4
4
 
5
- - Updated dropdown so onUpdate runs when dropdown is cleared
5
+ - Updated default colors for most components. Triggered by checkbox not that visible with
6
+ black backgrounds.
6
7
 
7
8
  0.1.96
8
9
 
9
- - Fixed issue with dropdown and using derived state.
10
+ - Fixed issue with dropdown and using derived state.
11
+
12
+ 0.1.95
13
+
14
+ - Updated dropdown so onUpdate runs when dropdown is cleared
@@ -24,7 +24,8 @@
24
24
  };
25
25
 
26
26
  // Merge partial colorScheme with defaults
27
- const colorScheme = { ...defaultColorScheme, ...partialColorScheme };
27
+ // svelte-ignore state_referenced_locally
28
+ const colorScheme = { ...defaultColorScheme, ...partialColorScheme };
28
29
  </script>
29
30
 
30
31
  {#if modalStatus}
@@ -19,14 +19,15 @@
19
19
  } = $props();
20
20
 
21
21
  const defaultColorScheme: Chart5ColorScheme = {
22
- bgColor: '#121212',
22
+ bgColor: '#1a191c',
23
23
  gridLineColor: '#303030',
24
24
  xAxisTextColor: '#5ac1dd',
25
25
  yAxisTextColor: '#5ac1dd'
26
26
  };
27
27
 
28
28
  // Merge partial colorScheme with defaults
29
- const colorScheme = { ...defaultColorScheme, ...partialColorScheme };
29
+ // svelte-ignore state_referenced_locally
30
+ const colorScheme = { ...defaultColorScheme, ...partialColorScheme };
30
31
 
31
32
  let yAxis = $state<number[]>([]);
32
33
  let maxY = $state<number>(0);
@@ -25,8 +25,8 @@
25
25
 
26
26
  const defaultColorScheme = {
27
27
  mainTextColor: '#D6D6D6',
28
- mainBgColor: '#121212',
29
- borderColor: '#262626',
28
+ mainBgColor: '#1a191c',
29
+ borderColor: '#3d3d3d',
30
30
  focusedColor: '#5ac1dd',
31
31
  checkmarkColor: '#5ac1dd'
32
32
  };
@@ -102,7 +102,8 @@
102
102
  .air5__checkbox__custom {
103
103
  width: 32px;
104
104
  height: 32px;
105
- border: 1px solid var(--air5__borderColor);
105
+ /* border: 1px solid var(--air5__borderColor); */
106
+ box-shadow: 0 0 0 2px var(--air5__borderColor);
106
107
  border-radius: 0.25rem;
107
108
  background-color: var(--air5__mainBgColor);
108
109
  display: flex;
@@ -38,7 +38,8 @@
38
38
 
39
39
  // Default colors
40
40
  const defaultColorScheme: ContextMenu5ColorScheme = {
41
- contentBgColor: '#121212',
41
+ contentBgColor: '#1a191c',
42
+ contentBorderColor: '#3d3d3d',
42
43
  iconFillColor: '#121212',
43
44
  iconBorderColor: '#121212',
44
45
  iconColor: '#F5F5F5'
@@ -34,13 +34,13 @@
34
34
  const defaultColorScheme: DatePicker5ColorScheme = {
35
35
  labelTextColor: '#989A9A',
36
36
  inputTextColor: '#D6D6D6',
37
- inputBgColor: '#121212',
38
- inputBorderColor: '#262626',
37
+ inputBgColor: '#1a191c',
38
+ inputBorderColor: '#3d3d3d',
39
39
  inputFocusedBorderColor: '#5ac1dd',
40
40
  inputClearColor: '#989A9A',
41
41
  inputClearHoverColor: '#1F2023',
42
42
  placeholderColor: '#46464A',
43
- pickerBgColor: '#121212',
43
+ pickerBgColor: '#1a191c',
44
44
  pickerMonthBgColor: '#121212',
45
45
  pickerTextColor: '#D6D6D6',
46
46
  pickerInactiveDayColor: '#46464A',
@@ -52,6 +52,7 @@
52
52
  };
53
53
 
54
54
  // Merge partial colorScheme with defaults
55
+ // svelte-ignore state_referenced_locally
55
56
  const colorScheme = { ...defaultColorScheme, ...partialColorScheme };
56
57
 
57
58
  // Normalize minDate to start of day
@@ -42,7 +42,8 @@
42
42
  };
43
43
 
44
44
  // Merge partial colorScheme with defaults
45
- const colorScheme = { ...defaultColorScheme, ...partialColorScheme };
45
+ // svelte-ignore state_referenced_locally
46
+ const colorScheme = { ...defaultColorScheme, ...partialColorScheme };
46
47
 
47
48
  let confirmed: boolean = $state(false);
48
49
  const menuId = `dialog5-${generateRandomString()}`;
@@ -1,17 +1,18 @@
1
1
  <script lang="ts">
2
2
  import { BROWSER } from 'esm-env';
3
- import { onDestroy, onMount } from 'svelte';
3
+ import { onMount } from 'svelte';
4
4
  import { fly } from 'svelte/transition';
5
5
  import type { DropDown5ColorScheme, DropDownItem } from './types.js';
6
6
 
7
+ // Props
7
8
  let {
8
9
  colorScheme: partialColorScheme = {},
9
10
  name = 'dropdown',
10
11
  title = 'Title',
11
- value = $bindable(),
12
- valueKey = $bindable(),
13
- extraValue = $bindable(),
14
- onUpdate = (key, v, ev) => {},
12
+ value = $bindable<any>(),
13
+ valueKey = $bindable<string | null | undefined>(),
14
+ extraValue = $bindable<string | null | undefined>(),
15
+ onUpdate = () => {},
15
16
  disabled = false,
16
17
  dropdownItems = [] as DropDownItem[]
17
18
  }: {
@@ -26,124 +27,147 @@
26
27
  dropdownItems?: DropDownItem[];
27
28
  } = $props();
28
29
 
30
+ // Default color scheme
29
31
  const defaultColorScheme: DropDown5ColorScheme = {
30
32
  textColor: '#D6D6D6',
31
- bgColor: '#121212',
32
- borderColor: '#262626',
33
+ bgColor: '#1a191c',
34
+ borderColor: '#3d3d3d',
33
35
  titleColor: '#989A9A',
34
- dropdownBgColor: '#141414',
36
+ dropdownBgColor: '#1a191c',
35
37
  focusedColor: '#5ac1dd',
36
38
  itemTextColor: '#D6D6D6',
37
39
  itemHoverBgColor: '#5ac1dd',
38
40
  itemHoverTextColor: '#121212'
39
41
  };
40
42
 
41
- // Merge partial colorScheme with defaults
42
- const colorScheme = { ...defaultColorScheme, ...partialColorScheme };
43
+ // Merge partial scheme with defaults
44
+ const colorScheme = $derived({ ...defaultColorScheme, ...partialColorScheme });
43
45
 
44
- const id = generateRandomString();
45
- let showDropdown = $state(false);
46
+ // Refs & state
47
+ let containerRef: HTMLDivElement | null = $state(null);
46
48
  let dropdownRef: HTMLDivElement | null = $state(null);
49
+ let showDropdown = $state(false);
47
50
  let filteredItems: DropDownItem[] = $state([]);
51
+ let highlightedIndex = $state(-1);
52
+ const id = generateRandomString();
48
53
 
54
+ // Effects
49
55
  $effect(() => {
50
56
  if (!BROWSER || !showDropdown || !dropdownRef) return;
57
+
51
58
  setTimeout(() => {
52
59
  if (!dropdownRef) return;
53
- const dropdownRect = dropdownRef.getBoundingClientRect();
60
+
61
+ const rect = dropdownRef.getBoundingClientRect();
54
62
  const viewportHeight = window.innerHeight;
55
- if (dropdownRect.bottom > viewportHeight) {
56
- const scrollY = window.scrollY + (dropdownRect.bottom - viewportHeight) + 10;
63
+
64
+ if (rect.bottom > viewportHeight) {
65
+ const scrollY = window.scrollY + (rect.bottom - viewportHeight) + 10;
57
66
  window.scrollTo({ top: scrollY, behavior: 'smooth' });
58
67
  }
59
68
  }, 0);
60
69
  });
61
70
 
62
- onMount(() => {
63
- document.addEventListener('click', closeDropdown);
64
- });
71
+ $effect(() => {
72
+ if (!showDropdown || !dropdownRef || highlightedIndex < 0) return;
65
73
 
66
- onDestroy(() => {
67
- try {
68
- document.removeEventListener('click', closeDropdown);
69
- } catch (error) {}
74
+ const items = dropdownRef.querySelectorAll<HTMLButtonElement>('button');
75
+ items[highlightedIndex]?.scrollIntoView({ block: 'nearest' });
70
76
  });
71
77
 
72
- /**
73
- * Generate a random string so that each
74
- * input will have a unique id in the dom
75
- */
76
- function generateRandomString() {
77
- const length = 6;
78
- const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
79
-
80
- let result = '';
81
- for (let i = 0; i < length; i++) {
82
- const randomIndex = Math.floor(Math.random() * characters.length);
83
- result += characters.charAt(randomIndex);
84
- }
78
+ // Outside click
79
+ function setupOutsideClick() {
80
+ if (!BROWSER) return;
81
+
82
+ const handler = (event: Event) => {
83
+ if (!containerRef?.contains(event.target as Node)) {
84
+ showDropdown = false;
85
+ validateInput();
86
+ }
87
+ };
85
88
 
86
- return result;
89
+ document.addEventListener('pointerdown', handler);
90
+ return () => document.removeEventListener('pointerdown', handler);
91
+ }
92
+
93
+ onMount(() => setupOutsideClick());
94
+
95
+ // Helpers
96
+ function generateRandomString(length = 6) {
97
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
98
+ return Array.from({ length }, () =>
99
+ chars.charAt(Math.floor(Math.random() * chars.length))
100
+ ).join('');
101
+ }
102
+
103
+ function handleBlur() {
104
+ setTimeout(() => {
105
+ if (!showDropdown) validateInput();
106
+ }, 100);
87
107
  }
88
108
 
89
- /**
90
- * Show the dropdown list
91
- */
92
109
  function showDropdownList() {
93
110
  showDropdown = true;
94
111
  filteredItems = dropdownItems;
112
+ highlightedIndex = filteredItems.length > 0 ? 0 : -1;
95
113
  }
96
114
 
97
- function closeDropdown(event: Event) {
98
- const target = event.target as HTMLElement;
99
-
100
- if (target.id !== `dropdown-${id}`) {
101
- showDropdown = false;
102
- validateInput();
103
- }
104
- }
105
115
  function filterItems() {
106
- filteredItems = dropdownItems.filter((item) =>
107
- item.value.toLowerCase().includes(value.toLowerCase())
108
- );
116
+ const search = (value ?? '').toLowerCase();
117
+ filteredItems = dropdownItems.filter((item) => item.value.toLowerCase().includes(search));
118
+ highlightedIndex = filteredItems.length > 0 ? 0 : -1;
109
119
  }
120
+
110
121
  function selectItem(item: DropDownItem) {
111
- // Set all bound values
112
122
  value = item.value;
113
123
  valueKey = item.id ?? '';
114
124
  extraValue = item.extraValue ?? '';
115
-
116
- // Notify parent
117
125
  onUpdate(valueKey, value, extraValue);
118
-
119
- // Close dropdown
120
126
  showDropdown = false;
127
+ highlightedIndex = -1;
121
128
  }
122
129
 
123
130
  function validateInput() {
124
- const matchedItem = dropdownItems.find(
125
- (item) => item.value.toLowerCase() === value.toLowerCase()
126
- );
127
-
128
- if (matchedItem) {
129
- // Valid match: sync values
130
- value = matchedItem.value;
131
- valueKey = matchedItem.id ?? '';
132
- extraValue = matchedItem.extraValue ?? '';
133
- } else {
134
- // No match: clear all
131
+ const search = (value ?? '').toLowerCase();
132
+ const match = dropdownItems.find((item) => item.value.toLowerCase() === search);
133
+
134
+ if (match) selectItem(match);
135
+ else {
135
136
  value = '';
136
137
  valueKey = '';
137
138
  extraValue = '';
139
+ onUpdate(valueKey, value, extraValue);
138
140
  }
141
+ }
139
142
 
140
- // Always notify parent of the current state
141
- onUpdate(valueKey, value, extraValue);
143
+ function handleKeydown(e: KeyboardEvent) {
144
+ if (!showDropdown) return;
145
+
146
+ if (e.key === 'ArrowDown') {
147
+ e.preventDefault();
148
+ highlightedIndex = Math.min(highlightedIndex + 1, filteredItems.length - 1);
149
+ }
150
+
151
+ if (e.key === 'ArrowUp') {
152
+ e.preventDefault();
153
+ highlightedIndex = Math.max(highlightedIndex - 1, 0);
154
+ }
155
+
156
+ if (e.key === 'Enter' && highlightedIndex >= 0) {
157
+ e.preventDefault();
158
+ selectItem(filteredItems[highlightedIndex]);
159
+ }
160
+
161
+ if (e.key === 'Escape') {
162
+ showDropdown = false;
163
+ highlightedIndex = -1;
164
+ }
142
165
  }
143
166
  </script>
144
167
 
145
168
  <div
146
169
  class="dropdown5__container"
170
+ bind:this={containerRef}
147
171
  style="
148
172
  --dropdown5__textColor: {colorScheme.textColor};
149
173
  --dropdown5__mainBgColor: {colorScheme.bgColor};
@@ -166,9 +190,10 @@
166
190
  aria-haspopup="listbox"
167
191
  {name}
168
192
  bind:value
193
+ onkeydown={handleKeydown}
169
194
  oninput={filterItems}
170
195
  onfocus={showDropdownList}
171
- onblur={validateInput}
196
+ onblur={handleBlur}
172
197
  autocomplete="off"
173
198
  {disabled}
174
199
  />
@@ -188,24 +213,22 @@
188
213
  role="listbox"
189
214
  aria-labelledby="dropdown-{id}-title"
190
215
  bind:this={dropdownRef}
191
- oninput={filterItems}
192
- transition:fly={{
193
- y: 10
194
- }}
216
+ transition:fly={{ y: 10 }}
195
217
  >
196
218
  <div style="height:100%;">
197
219
  {#each filteredItems as item, index}
198
220
  <button
199
221
  type="button"
200
222
  class="dropdown5__list__item"
223
+ class:selected={index === highlightedIndex}
201
224
  role="option"
202
225
  aria-selected={item.value === value ? 'true' : 'false'}
203
- onclick={() => selectItem(item)}
226
+ onmousedown={() => selectItem(item)}
204
227
  >
205
- {#if item.value === value}
206
- <div class="fic">
207
- <div style="padding-right: 0.5rem;">
208
- <div class="fca" style="width: 1rem; height: 1rem;">
228
+ <div class="fic">
229
+ <div style="padding-right: 0.5rem;">
230
+ <div class="fca" style="width: 1rem; height: 1rem;">
231
+ {#if item.value === value}
209
232
  <svg
210
233
  viewBox="0 0 12 12"
211
234
  xmlns="http://www.w3.org/2000/svg"
@@ -213,36 +236,24 @@
213
236
  role="presentation"
214
237
  focusable="false"
215
238
  style="display: block; height: 12px; width: 12px; fill: currentcolor;"
216
- ><path
217
- d="m10.5 1.939 1.061 1.061-7.061 7.061-.53-.531-3-3-.531-.53 1.061-1.061 3 3 5.47-5.469z"
218
- ></path></svg
219
239
  >
220
- </div>
240
+ <path
241
+ d="m10.5 1.939 1.061 1.061-7.061 7.061-.53-.531-3-3-.531-.53 1.061-1.061 3 3 5.47-5.469z"
242
+ ></path>
243
+ </svg>
244
+ {/if}
221
245
  </div>
222
- {#if item.component}
223
- {@const Component = item.component}
224
- <div class="component" style={item.componentStyles}>
225
- <Component {...item.props} />
226
- </div>
227
- {/if}
228
- <div>{item.value}</div>
229
246
  </div>
230
- {:else}
231
- <div class="fic">
232
- <div style="padding-right: 0.5rem;">
233
- <div style="width: 1rem; height: 1rem;"></div>
234
- </div>
235
247
 
236
- {#if item.component}
237
- {@const Component = item.component}
238
- <div class="component" style={item.componentStyles}>
239
- <Component {...item.props} />
240
- </div>
241
- {/if}
248
+ {#if item.component}
249
+ {@const Component = item.component}
250
+ <div class="component" style={item.componentStyles}>
251
+ <Component {...item.props} />
252
+ </div>
253
+ {/if}
242
254
 
243
- <div>{item.value}</div>
244
- </div>
245
- {/if}
255
+ <div>{item.value}</div>
256
+ </div>
246
257
  </button>
247
258
  {/each}
248
259
  </div>
@@ -332,4 +343,9 @@
332
343
  width: 2rem;
333
344
  height: 2rem;
334
345
  }
346
+
347
+ .selected {
348
+ background-color: var(--dropdown5__itemHoverBgColor);
349
+ color: var(--dropdown5__itemHoverTextColor);
350
+ }
335
351
  </style>
@@ -42,9 +42,9 @@
42
42
 
43
43
  const defaultColorScheme: Input5ColorScheme = {
44
44
  mainTextColor: '#D6D6D6',
45
- mainBgColor: '#121212',
45
+ mainBgColor: '#1a191c',
46
46
  titleColor: '#989A9A',
47
- borderColor: '#262626',
47
+ borderColor: '#3D3D3D',
48
48
  focusedColor: '#5ac1dd',
49
49
  clearColor: '#989A9A',
50
50
  clearHoverColor: '#1F2023',
@@ -3,10 +3,6 @@
3
3
  import { fly } from 'svelte/transition';
4
4
  import type { TimePicker5ColorScheme } from './types.js';
5
5
 
6
- /**
7
- * Time Picker 5 v.0.0.1
8
- */
9
-
10
6
  let {
11
7
  colorScheme: partialColorScheme = {},
12
8
  timeText = $bindable(),
@@ -31,16 +27,17 @@
31
27
 
32
28
  const defaultColorScheme: TimePicker5ColorScheme = {
33
29
  textColor: '#D6D6D6',
34
- bgColor: '#121212',
35
- borderColor: '#262626',
30
+ bgColor: '#1a191c',
31
+ borderColor: '#3d3d3d',
36
32
  focusedColor: '#5ac1dd',
37
33
  titleColor: '#989A9A',
38
34
  clearColor: '#989A9A',
39
35
  clearHoverColor: '#1F2023',
40
- dropdownBgColor: '#141414'
36
+ dropdownBgColor: '#1a191c'
41
37
  };
42
38
 
43
39
  // Merge partial colorScheme with defaults
40
+ // svelte-ignore state_referenced_locally
44
41
  const colorScheme = { ...defaultColorScheme, ...partialColorScheme };
45
42
 
46
43
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djcali570/component-lib",
3
- "version": "0.1.96",
3
+ "version": "0.1.98",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",