@djcali570/component-lib 0.0.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/README.md +58 -0
- package/dist/DatePicker5.svelte +625 -0
- package/dist/DatePicker5.svelte.d.ts +17 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/package.json +68 -0
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Svelte library
|
|
2
|
+
|
|
3
|
+
Everything you need to build a Svelte library, powered by [`sv`](https://npmjs.com/package/sv).
|
|
4
|
+
|
|
5
|
+
Read more about creating a library [in the docs](https://svelte.dev/docs/kit/packaging).
|
|
6
|
+
|
|
7
|
+
## Creating a project
|
|
8
|
+
|
|
9
|
+
If you're seeing this, you've probably already done this step. Congrats!
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# create a new project in the current directory
|
|
13
|
+
npx sv create
|
|
14
|
+
|
|
15
|
+
# create a new project in my-app
|
|
16
|
+
npx sv create my-app
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Developing
|
|
20
|
+
|
|
21
|
+
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm run dev
|
|
25
|
+
|
|
26
|
+
# or start the server and open the app in a new browser tab
|
|
27
|
+
npm run dev -- --open
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Everything inside `src/lib` is part of your library, everything inside `src/routes` can be used as a showcase or preview app.
|
|
31
|
+
|
|
32
|
+
## Building
|
|
33
|
+
|
|
34
|
+
To build your library:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm run package
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
To create a production version of your showcase app:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm run build
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
You can preview the production build with `npm run preview`.
|
|
47
|
+
|
|
48
|
+
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
|
|
49
|
+
|
|
50
|
+
## Publishing
|
|
51
|
+
|
|
52
|
+
Go into the `package.json` and give your package the desired name through the `"name"` option. Also consider adding a `"license"` field and point it to a `LICENSE` file which you can create from a template (one popular option is the [MIT license](https://opensource.org/license/mit/)).
|
|
53
|
+
|
|
54
|
+
To publish your library to [npm](https://www.npmjs.com):
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm publish
|
|
58
|
+
```
|
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { browser } from '$app/environment';
|
|
3
|
+
import { fly } from 'svelte/transition';
|
|
4
|
+
|
|
5
|
+
// Props with runes
|
|
6
|
+
let {
|
|
7
|
+
colorScheme = {
|
|
8
|
+
labelTextColor: '#989A9A',
|
|
9
|
+
inputTextColor: '#D6D6D6',
|
|
10
|
+
inputBgColor: '#121212',
|
|
11
|
+
inputBorderColor: '#262626',
|
|
12
|
+
inputFocusedBorderColor: '#4787ac',
|
|
13
|
+
inputClearColor: '#989A9A',
|
|
14
|
+
inputClearHoverColor: '#1F2023',
|
|
15
|
+
placeholderColor: '#46464A',
|
|
16
|
+
pickerBgColor: '#121212',
|
|
17
|
+
pickerMonthBgColor: '#121212',
|
|
18
|
+
pickerTextColor: '#D6D6D6',
|
|
19
|
+
pickerInactiveDayColor: '#46464A',
|
|
20
|
+
pickerTodayColor: '#BF3131',
|
|
21
|
+
pickerSelectedDayBgColor: '#4787ac',
|
|
22
|
+
pickerSelectedDayTextColor: '#121212',
|
|
23
|
+
pickerChevronHoverColor: '#1F2023',
|
|
24
|
+
pickerMaxHeight: '340px'
|
|
25
|
+
},
|
|
26
|
+
title = '',
|
|
27
|
+
name = '',
|
|
28
|
+
dateText = $bindable(),
|
|
29
|
+
placeholder = '',
|
|
30
|
+
minDate = new Date(),
|
|
31
|
+
maxDate = null,
|
|
32
|
+
blockedDates = [],
|
|
33
|
+
radius = 'full',
|
|
34
|
+
position = 'left-0',
|
|
35
|
+
disabled = false,
|
|
36
|
+
onDateUpdate = (date: string) => {}
|
|
37
|
+
}: {
|
|
38
|
+
title: string;
|
|
39
|
+
name: string;
|
|
40
|
+
dateText?: string;
|
|
41
|
+
placeholder?: string;
|
|
42
|
+
minDate?: Date;
|
|
43
|
+
maxDate?: Date | null;
|
|
44
|
+
blockedDates?: Date[];
|
|
45
|
+
radius?: 'right' | 'left' | 'full';
|
|
46
|
+
position?: 'left-0' | 'right-0';
|
|
47
|
+
disabled?: boolean;
|
|
48
|
+
colorScheme?: Record<string, string>;
|
|
49
|
+
onDateUpdate?: (date: string) => void;
|
|
50
|
+
} = $props();
|
|
51
|
+
|
|
52
|
+
// Normalize minDate to start of day
|
|
53
|
+
minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate());
|
|
54
|
+
|
|
55
|
+
// State with runes
|
|
56
|
+
let showDropdown = $state(false);
|
|
57
|
+
let viewDate = $state(dateText ? new Date(dateText) : new Date());
|
|
58
|
+
let today = $state(new Date());
|
|
59
|
+
let selectedDate = $state(dateText ? new Date(dateText) : null);
|
|
60
|
+
let calendarDays = $state<(Date | null)[]>([]);
|
|
61
|
+
let dropdownRef = $state<HTMLElement | null>(null); // Reference to the dropdown
|
|
62
|
+
let inputRef = $state<HTMLElement | null>(null); // Reference to the input container
|
|
63
|
+
let showPrevMonthButton = $state(true);
|
|
64
|
+
let showNextMonthButton = $state(true);
|
|
65
|
+
|
|
66
|
+
// Static data
|
|
67
|
+
const weekAbv = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
|
|
68
|
+
const monthNames = [
|
|
69
|
+
'January',
|
|
70
|
+
'February',
|
|
71
|
+
'March',
|
|
72
|
+
'April',
|
|
73
|
+
'May',
|
|
74
|
+
'June',
|
|
75
|
+
'July',
|
|
76
|
+
'August',
|
|
77
|
+
'September',
|
|
78
|
+
'October',
|
|
79
|
+
'November',
|
|
80
|
+
'December'
|
|
81
|
+
];
|
|
82
|
+
const calendarGridCount = 42;
|
|
83
|
+
const id = generateRandomString();
|
|
84
|
+
|
|
85
|
+
// Default maxDate
|
|
86
|
+
if (!maxDate && minDate) {
|
|
87
|
+
maxDate = new Date(minDate);
|
|
88
|
+
maxDate.setFullYear(minDate.getFullYear() + 1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Initial call to populate calendarDays
|
|
92
|
+
getDays();
|
|
93
|
+
|
|
94
|
+
// Reactive updates for dateText
|
|
95
|
+
$effect(() => {
|
|
96
|
+
if (dateText) {
|
|
97
|
+
const newDate = new Date(dateText);
|
|
98
|
+
if (
|
|
99
|
+
!isNaN(newDate.getTime()) &&
|
|
100
|
+
dateText !==
|
|
101
|
+
selectedDate?.toLocaleDateString('en-US', {
|
|
102
|
+
year: 'numeric',
|
|
103
|
+
month: '2-digit',
|
|
104
|
+
day: '2-digit'
|
|
105
|
+
})
|
|
106
|
+
) {
|
|
107
|
+
selectedDate = newDate;
|
|
108
|
+
viewDate = new Date(newDate.getFullYear(), newDate.getMonth(), 1);
|
|
109
|
+
}
|
|
110
|
+
} else if (selectedDate !== null) {
|
|
111
|
+
selectedDate = null;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Close dropdown when clicking outside
|
|
116
|
+
$effect(() => {
|
|
117
|
+
if (!browser || !showDropdown) return;
|
|
118
|
+
|
|
119
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
120
|
+
const target = event.target as Node;
|
|
121
|
+
if (dropdownRef && inputRef && !dropdownRef.contains(target) && !inputRef.contains(target)) {
|
|
122
|
+
showDropdown = false;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
document.addEventListener('click', handleClickOutside);
|
|
127
|
+
|
|
128
|
+
return () => {
|
|
129
|
+
document.removeEventListener('click', handleClickOutside);
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Refresh calendar when minDate or maxDate changes
|
|
134
|
+
$effect(() => {
|
|
135
|
+
minDate;
|
|
136
|
+
maxDate;
|
|
137
|
+
getDays();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Auto-scroll to make dropdown fully visible
|
|
141
|
+
|
|
142
|
+
$effect(() => {
|
|
143
|
+
if (!browser || !showDropdown || !dropdownRef) return;
|
|
144
|
+
setTimeout(() => {
|
|
145
|
+
if (!dropdownRef) return;
|
|
146
|
+
const dropdownRect = dropdownRef.getBoundingClientRect();
|
|
147
|
+
const viewportHeight = window.innerHeight;
|
|
148
|
+
if (dropdownRect.bottom > viewportHeight) {
|
|
149
|
+
const scrollY = window.scrollY + (dropdownRect.bottom - viewportHeight) + 10;
|
|
150
|
+
window.scrollTo({ top: scrollY, behavior: 'smooth' });
|
|
151
|
+
}
|
|
152
|
+
}, 0);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
function getDays() {
|
|
156
|
+
const year = viewDate.getFullYear();
|
|
157
|
+
const month = viewDate.getMonth();
|
|
158
|
+
|
|
159
|
+
// Current month days
|
|
160
|
+
const lastDayOfMonth = new Date(year, month + 1, 0).getDate();
|
|
161
|
+
let daysOfMonth: Date[] = [];
|
|
162
|
+
for (let day = 1; day <= lastDayOfMonth; day++) {
|
|
163
|
+
daysOfMonth.push(new Date(year, month, day));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Initialize calendarDays
|
|
167
|
+
let calendarDaysTemp = Array(calendarGridCount).fill(null);
|
|
168
|
+
|
|
169
|
+
// First day's weekday
|
|
170
|
+
const firstDayOfMonth = new Date(year, month, 1);
|
|
171
|
+
const firstDayOfWeekValue = firstDayOfMonth.getDay();
|
|
172
|
+
|
|
173
|
+
// Previous month padding
|
|
174
|
+
const prevMonthLastDay = new Date(year, month, 0).getDate();
|
|
175
|
+
const prevMonthStart = prevMonthLastDay - firstDayOfWeekValue + 1;
|
|
176
|
+
for (let i = 0; i < firstDayOfWeekValue; i++) {
|
|
177
|
+
calendarDaysTemp[i] = new Date(year, month - 1, prevMonthStart + i);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Current month days
|
|
181
|
+
calendarDaysTemp = replaceCellsWithDays(calendarDaysTemp, daysOfMonth, firstDayOfWeekValue);
|
|
182
|
+
|
|
183
|
+
// Next month padding
|
|
184
|
+
const filledCells = firstDayOfWeekValue + daysOfMonth.length;
|
|
185
|
+
for (let i = filledCells, day = 1; i < calendarGridCount; i++, day++) {
|
|
186
|
+
calendarDaysTemp[i] = new Date(year, month + 1, day);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Update month button states
|
|
190
|
+
showPrevMonthButton =
|
|
191
|
+
year < minDate.getFullYear() ||
|
|
192
|
+
(year === minDate.getFullYear() && month <= minDate.getMonth());
|
|
193
|
+
showNextMonthButton = maxDate
|
|
194
|
+
? year > maxDate.getFullYear() ||
|
|
195
|
+
(year === maxDate.getFullYear() && month >= maxDate.getMonth())
|
|
196
|
+
: false;
|
|
197
|
+
|
|
198
|
+
calendarDays = calendarDaysTemp;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Fill the calendarDays array
|
|
203
|
+
*/
|
|
204
|
+
function replaceCellsWithDays(cells: (Date | null)[], days: Date[], startPoint: number) {
|
|
205
|
+
const newCells = [...cells];
|
|
206
|
+
|
|
207
|
+
// Place days starting at startPoint
|
|
208
|
+
for (let i = 0; i < days.length; i++) {
|
|
209
|
+
if (startPoint + i < newCells.length) {
|
|
210
|
+
newCells[startPoint + i] = days[i];
|
|
211
|
+
} else {
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return newCells;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function clearDate() {
|
|
220
|
+
dateText = '';
|
|
221
|
+
selectedDate = null;
|
|
222
|
+
showDropdown = false;
|
|
223
|
+
getDays();
|
|
224
|
+
onDateUpdate('');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function prevMonth() {
|
|
228
|
+
viewDate = new Date(viewDate.getFullYear(), viewDate.getMonth() - 1, 1);
|
|
229
|
+
getDays();
|
|
230
|
+
}
|
|
231
|
+
function nextMonth() {
|
|
232
|
+
viewDate = new Date(viewDate.getFullYear(), viewDate.getMonth() + 1, 1);
|
|
233
|
+
getDays();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function selectDate(date: Date) {
|
|
237
|
+
selectedDate = date;
|
|
238
|
+
viewDate = new Date(date.getFullYear(), date.getMonth(), 1);
|
|
239
|
+
const newDateText = date.toLocaleDateString('en-US', {
|
|
240
|
+
year: 'numeric',
|
|
241
|
+
month: '2-digit',
|
|
242
|
+
day: '2-digit'
|
|
243
|
+
});
|
|
244
|
+
dateText = newDateText;
|
|
245
|
+
getDays();
|
|
246
|
+
showDropdown = false;
|
|
247
|
+
onDateUpdate(newDateText);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Generate a random string so that each
|
|
252
|
+
* input will have a unique id in the dom
|
|
253
|
+
*/
|
|
254
|
+
function generateRandomString() {
|
|
255
|
+
const length = 6;
|
|
256
|
+
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
257
|
+
|
|
258
|
+
let result = '';
|
|
259
|
+
for (let i = 0; i < length; i++) {
|
|
260
|
+
const randomIndex = Math.floor(Math.random() * characters.length);
|
|
261
|
+
result += characters.charAt(randomIndex);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// blockedDates = [
|
|
268
|
+
// new Date(2025, 3, 15), // April 15, 2025
|
|
269
|
+
// new Date(2025, 3, 20), // April 20, 2025
|
|
270
|
+
// new Date(2025, 4, 5) // May 5, 2025
|
|
271
|
+
// ];
|
|
272
|
+
</script>
|
|
273
|
+
|
|
274
|
+
<div
|
|
275
|
+
class="picker"
|
|
276
|
+
style="
|
|
277
|
+
--dpicker-inputBgColor: {colorScheme.inputBgColor};
|
|
278
|
+
--dpicker-borderColor: {colorScheme.inputBorderColor};
|
|
279
|
+
--dpicker-textColor: {colorScheme.inputTextColor};
|
|
280
|
+
--dpicker-labelTextColor: {colorScheme.labelTextColor};
|
|
281
|
+
--dpicker-clearColor: {colorScheme.inputClearColor};
|
|
282
|
+
--dpicker-clearHoverColor: {colorScheme.inputClearHoverColor};
|
|
283
|
+
--dpicker-maxHeight: {colorScheme.pickerMaxHeight};
|
|
284
|
+
--dpicker-bgColor: {colorScheme.pickerBgColor};
|
|
285
|
+
--dpicker-inputFocusedBorderColor: {colorScheme.inputFocusedBorderColor};
|
|
286
|
+
--dpicker-monthBgColor: {colorScheme.pickerMonthBgColor};
|
|
287
|
+
--dpicker-selectedDayBgColor: {colorScheme.pickerSelectedDayBgColor};
|
|
288
|
+
--dpicker-selectedDayTextColor: {colorScheme.pickerSelectedDayTextColor};
|
|
289
|
+
--dpicker-inactiveDayColor: {colorScheme.pickerInactiveDayColor};
|
|
290
|
+
"
|
|
291
|
+
>
|
|
292
|
+
<div
|
|
293
|
+
class="outer"
|
|
294
|
+
bind:this={inputRef}
|
|
295
|
+
role="button"
|
|
296
|
+
tabindex={disabled ? -1 : 0}
|
|
297
|
+
class:disabled-input={disabled}
|
|
298
|
+
class:radius--l--only={radius === 'left'}
|
|
299
|
+
class:radius--r--only={radius === 'right'}
|
|
300
|
+
class:radius--full={radius === 'full'}
|
|
301
|
+
onclick={() => !disabled && (showDropdown = true)}
|
|
302
|
+
onkeydown={(e) =>
|
|
303
|
+
!disabled && (e.key === 'Enter' || e.key === 'Space') && (showDropdown = true)}
|
|
304
|
+
>
|
|
305
|
+
<label for="dpicker-{id}">
|
|
306
|
+
<div class="dpicker-title">
|
|
307
|
+
<div>{title}</div>
|
|
308
|
+
</div>
|
|
309
|
+
<div>
|
|
310
|
+
<div class="dpicker-input">
|
|
311
|
+
<input
|
|
312
|
+
type="text"
|
|
313
|
+
aria-label={name}
|
|
314
|
+
aria-describedby={name}
|
|
315
|
+
id="dpicker-input-{id}"
|
|
316
|
+
autocomplete="off"
|
|
317
|
+
{placeholder}
|
|
318
|
+
{name}
|
|
319
|
+
readonly
|
|
320
|
+
bind:value={dateText}
|
|
321
|
+
/>
|
|
322
|
+
<div class="flex justify-center items-center p-3">
|
|
323
|
+
{#if dateText && !disabled}
|
|
324
|
+
<button
|
|
325
|
+
aria-label="Close Date Picker"
|
|
326
|
+
id="dpicker-CloseBtn-{id}"
|
|
327
|
+
class="dpicker-closeBtn w-6 h-6 flex justify-center items-center rounded-full"
|
|
328
|
+
onclick={(e) => {
|
|
329
|
+
e.stopPropagation();
|
|
330
|
+
clearDate();
|
|
331
|
+
}}
|
|
332
|
+
>
|
|
333
|
+
<div class="h-[14px] w-[14px] close-btn">
|
|
334
|
+
<svg
|
|
335
|
+
version="1.1"
|
|
336
|
+
id="Layer_1"
|
|
337
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
338
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
339
|
+
x="0px"
|
|
340
|
+
y="0px"
|
|
341
|
+
viewBox="0 0 100 100"
|
|
342
|
+
style="enable-background:new 0 0 100 100;"
|
|
343
|
+
xml:space="preserve"
|
|
344
|
+
>
|
|
345
|
+
<path
|
|
346
|
+
fill="currentColor"
|
|
347
|
+
d="M87.8,87.4c-0.7,0.7-1.6,1.1-2.6,1.1c-0.9,0-1.8-0.4-2.5-1.1L50,55.1L17.8,87.8c-0.7,0.7-1.6,1.1-2.6,1.1
|
|
348
|
+
c-0.9,0-1.8-0.3-2.5-1.1c-1.4-1.4-1.4-3.7,0-5.1L44.9,50L12.2,17.8c-1.4-1.4-1.4-3.7,0-5.1c1.4-1.4,3.7-1.4,5.1,0L50,44.9l32.3-32.6
|
|
349
|
+
c1.4-1.4,3.7-1.4,5.1,0c1.4,1.4,1.4,3.7,0,5.1L55.1,50l32.6,32.3C89.2,83.7,89.2,86,87.8,87.4z"
|
|
350
|
+
/>
|
|
351
|
+
</svg>
|
|
352
|
+
</div>
|
|
353
|
+
</button>
|
|
354
|
+
{/if}
|
|
355
|
+
</div>
|
|
356
|
+
</div>
|
|
357
|
+
</div>
|
|
358
|
+
</label>
|
|
359
|
+
</div>
|
|
360
|
+
{#if showDropdown && !disabled}
|
|
361
|
+
<div
|
|
362
|
+
bind:this={dropdownRef}
|
|
363
|
+
class="dpicker-dropdown {position}"
|
|
364
|
+
style="box-shadow: 0px 1px 16px 0px rgba(0,0,0,0.12);"
|
|
365
|
+
transition:fly={{ y: 10 }}
|
|
366
|
+
>
|
|
367
|
+
<div class="h-full">
|
|
368
|
+
<div class="grid grid-cols-7 month-block">
|
|
369
|
+
<button
|
|
370
|
+
id="chev-prev-{id}"
|
|
371
|
+
type="button"
|
|
372
|
+
aria-label="Previous Month"
|
|
373
|
+
class="w-full flex col-span-1 items-center justify-center cursor-pointer"
|
|
374
|
+
disabled={showPrevMonthButton}
|
|
375
|
+
onclick={prevMonth}
|
|
376
|
+
>
|
|
377
|
+
<div
|
|
378
|
+
class="size-8 flex justify-center items-center rounded-full chev"
|
|
379
|
+
class:disabled={showPrevMonthButton}
|
|
380
|
+
>
|
|
381
|
+
<div class="size-5">
|
|
382
|
+
<svg
|
|
383
|
+
version="1.1"
|
|
384
|
+
id="Layer_1"
|
|
385
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
386
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
387
|
+
x="0px"
|
|
388
|
+
y="0px"
|
|
389
|
+
viewBox="0 0 200 200"
|
|
390
|
+
style="enable-background:new 0 0 200 200;"
|
|
391
|
+
xml:space="preserve"
|
|
392
|
+
>
|
|
393
|
+
<path
|
|
394
|
+
fill="currentColor"
|
|
395
|
+
d="M63.6,103.4c-1.9-1.9-1.9-5.1,0-7.1L129,30.9c1-1,2.2-1.5,3.5-1.5c1.3,0,2.6,0.5,3.5,1.5c1.9,1.9,1.9,5.1,0,7.1L74.3,99.8
|
|
396
|
+
l62.1,62.1c1.9,1.9,1.9,5.1,0,7.1l-0.1,0.1c-1.1,0.9-2.3,1.4-3.6,1.4c-1.3,0-2.6-0.5-3.5-1.5L63.6,103.4z"
|
|
397
|
+
/>
|
|
398
|
+
</svg>
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
</button>
|
|
402
|
+
<div class="col-span-5 flex justify-center items-center">
|
|
403
|
+
{monthNames[viewDate.getMonth()]}
|
|
404
|
+
{viewDate.getFullYear()}
|
|
405
|
+
</div>
|
|
406
|
+
<button
|
|
407
|
+
id="chev-next-{id}"
|
|
408
|
+
type="button"
|
|
409
|
+
aria-label="Next Month"
|
|
410
|
+
class="w-full flex col-span-1 items-center justify-center cursor-pointer"
|
|
411
|
+
disabled={showNextMonthButton}
|
|
412
|
+
onclick={nextMonth}
|
|
413
|
+
>
|
|
414
|
+
<div
|
|
415
|
+
class="size-8 flex justify-center items-center rounded-full chev"
|
|
416
|
+
class:disabled={showNextMonthButton}
|
|
417
|
+
>
|
|
418
|
+
<div class="size-5">
|
|
419
|
+
<svg
|
|
420
|
+
version="1.1"
|
|
421
|
+
id="Layer_1"
|
|
422
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
423
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
424
|
+
x="0px"
|
|
425
|
+
y="0px"
|
|
426
|
+
viewBox="0 0 200 200"
|
|
427
|
+
style="enable-background:new 0 0 200 200;"
|
|
428
|
+
xml:space="preserve"
|
|
429
|
+
>
|
|
430
|
+
<path
|
|
431
|
+
fill="currentColor"
|
|
432
|
+
d="M136.4,96.6c1.9,1.9,1.9,5.1,0,7.1L71,169.1c-1,1-2.2,1.5-3.5,1.5c-1.3,0-2.6-0.5-3.5-1.5c-1.9-1.9-1.9-5.1,0-7.1l61.8-61.8
|
|
433
|
+
L63.6,38c-1.9-1.9-1.9-5.1,0-7.1l0.1-0.1c1.1-0.9,2.3-1.4,3.6-1.4c1.3,0,2.6,0.5,3.5,1.5L136.4,96.6z"
|
|
434
|
+
/>
|
|
435
|
+
</svg>
|
|
436
|
+
</div>
|
|
437
|
+
</div>
|
|
438
|
+
</button>
|
|
439
|
+
</div>
|
|
440
|
+
<div class="grid grid-cols-7">
|
|
441
|
+
{#each weekAbv as weekday}
|
|
442
|
+
<div class="flex justify-center items-center text-[var(--text-light)] text-sm">
|
|
443
|
+
{weekday}
|
|
444
|
+
</div>
|
|
445
|
+
{/each}
|
|
446
|
+
</div>
|
|
447
|
+
<div class="grid grid-cols-7 grid-rows-6">
|
|
448
|
+
{#each calendarDays as date, index (index)}
|
|
449
|
+
<div id="day-{index}" class="flex justify-center items-center">
|
|
450
|
+
{#if date}
|
|
451
|
+
<button
|
|
452
|
+
id="day-btn-{index}"
|
|
453
|
+
type="button"
|
|
454
|
+
class="h-10 w-10 flex justify-center items-center calendar-day"
|
|
455
|
+
class:highlighted={(selectedDate &&
|
|
456
|
+
date.toDateString() === selectedDate.toDateString()) ||
|
|
457
|
+
(!selectedDate && date.toDateString() === today.toDateString())}
|
|
458
|
+
class:other-month={date.getMonth() !== viewDate.getMonth()}
|
|
459
|
+
class:blocked={blockedDates.some((d) => d.toDateString() === date.toDateString())}
|
|
460
|
+
data-index={index}
|
|
461
|
+
aria-label={`Select ${date.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' })}`}
|
|
462
|
+
disabled={date < minDate ||
|
|
463
|
+
(maxDate && date > maxDate) ||
|
|
464
|
+
blockedDates.some((d) => d.toDateString() === date.toDateString())}
|
|
465
|
+
onclick={() => selectDate(date)}
|
|
466
|
+
>
|
|
467
|
+
{date.getDate()}
|
|
468
|
+
</button>
|
|
469
|
+
{:else}
|
|
470
|
+
<div class="h-10 w-10 flex justify-center items-center"></div>
|
|
471
|
+
{/if}
|
|
472
|
+
</div>
|
|
473
|
+
{/each}
|
|
474
|
+
</div>
|
|
475
|
+
</div>
|
|
476
|
+
</div>
|
|
477
|
+
{/if}
|
|
478
|
+
</div>
|
|
479
|
+
|
|
480
|
+
<style>
|
|
481
|
+
.picker {
|
|
482
|
+
position: relative;
|
|
483
|
+
display: inline-block;
|
|
484
|
+
width: 100%;
|
|
485
|
+
color: var(--dpicker-textColor);
|
|
486
|
+
}
|
|
487
|
+
.outer {
|
|
488
|
+
background-color: var(--dpicker-inputBgColor);
|
|
489
|
+
border: none;
|
|
490
|
+
line-height: 20px;
|
|
491
|
+
font-size: 16px;
|
|
492
|
+
margin: 0;
|
|
493
|
+
box-shadow: inset 0 0 0 1px var(--dpicker-borderColor);
|
|
494
|
+
width: 100%;
|
|
495
|
+
display: flex;
|
|
496
|
+
position: relative;
|
|
497
|
+
min-height: 62px;
|
|
498
|
+
text-size-adjust: 100%;
|
|
499
|
+
}
|
|
500
|
+
label {
|
|
501
|
+
line-height: 20px;
|
|
502
|
+
color: var(--dpicker-textColor);
|
|
503
|
+
flex-grow: 1;
|
|
504
|
+
flex-basis: 0%;
|
|
505
|
+
flex-shrink: 1;
|
|
506
|
+
position: relative;
|
|
507
|
+
box-sizing: border-box;
|
|
508
|
+
}
|
|
509
|
+
.dpicker-title {
|
|
510
|
+
line-height: 20px;
|
|
511
|
+
color: var(--dpicker-labelTextColor);
|
|
512
|
+
display: block;
|
|
513
|
+
left: 12px;
|
|
514
|
+
right: 12px;
|
|
515
|
+
position: absolute;
|
|
516
|
+
top: 18px;
|
|
517
|
+
transform: matrix(0.75, 0, 0, 0.75, 0, -8);
|
|
518
|
+
transform-origin: 0px 0px;
|
|
519
|
+
transition: transform 0.15s cubic-bezier(0.455, 0.03, 0.515, 0.955);
|
|
520
|
+
}
|
|
521
|
+
input {
|
|
522
|
+
background-color: transparent;
|
|
523
|
+
border: none;
|
|
524
|
+
font-family: inherit;
|
|
525
|
+
padding: 0;
|
|
526
|
+
line-height: inherit;
|
|
527
|
+
margin: 26px 12px 10px;
|
|
528
|
+
height: 20px;
|
|
529
|
+
width: 100%;
|
|
530
|
+
outline: none;
|
|
531
|
+
color: inherit;
|
|
532
|
+
appearance: none;
|
|
533
|
+
-webkit-appearance: none;
|
|
534
|
+
}
|
|
535
|
+
.dpicker-input {
|
|
536
|
+
display: flex;
|
|
537
|
+
opacity: 1;
|
|
538
|
+
}
|
|
539
|
+
.radius--full {
|
|
540
|
+
border-radius: 0.5rem /* 8px */;
|
|
541
|
+
}
|
|
542
|
+
.radius--l--only {
|
|
543
|
+
border-top-left-radius: 0.5rem /* 8px */;
|
|
544
|
+
border-bottom-left-radius: 0.5rem /* 8px */;
|
|
545
|
+
}
|
|
546
|
+
.radius--r--only {
|
|
547
|
+
border-top-right-radius: 0.5rem /* 8px */;
|
|
548
|
+
border-bottom-right-radius: 0.5rem /* 8px */;
|
|
549
|
+
}
|
|
550
|
+
.dpicker-closeBtn {
|
|
551
|
+
display: none;
|
|
552
|
+
color: var(--dpicker-clearColor);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
.dpicker-closeBtn:hover {
|
|
556
|
+
background-color: var(--dpicker-clearHoverColor);
|
|
557
|
+
}
|
|
558
|
+
.outer:focus-within .dpicker-closeBtn {
|
|
559
|
+
display: flex;
|
|
560
|
+
}
|
|
561
|
+
.outer:not(.disabled-input):focus-within {
|
|
562
|
+
box-shadow: inset 0 0 0 2px var(--dpicker-inputFocusedBorderColor);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.dpicker-dropdown {
|
|
566
|
+
position: absolute;
|
|
567
|
+
display: flex;
|
|
568
|
+
flex-direction: column;
|
|
569
|
+
top: 110%;
|
|
570
|
+
z-index: 20;
|
|
571
|
+
overflow-y: auto;
|
|
572
|
+
border-radius: 0.5rem /* 8px */;
|
|
573
|
+
max-height: var(--dpicker-maxHeight);
|
|
574
|
+
width: 100%;
|
|
575
|
+
min-width: 300px;
|
|
576
|
+
background-color: var(--dpicker-bgColor);
|
|
577
|
+
}
|
|
578
|
+
.month-block {
|
|
579
|
+
width: 100%;
|
|
580
|
+
background-color: var(--picker-monthBgColor);
|
|
581
|
+
}
|
|
582
|
+
.chev:hover {
|
|
583
|
+
background-color: var(--dpicker-clearHoverColor);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.chev.disabled {
|
|
587
|
+
color: var(--dpicker-inactiveDayColor);
|
|
588
|
+
}
|
|
589
|
+
.calendar-day {
|
|
590
|
+
color: var(--dpicker-textColor);
|
|
591
|
+
border-radius: 50%;
|
|
592
|
+
}
|
|
593
|
+
.calendar-day:hover:not(:disabled) {
|
|
594
|
+
border: 1px solid var(--dpicker-textColor);
|
|
595
|
+
}
|
|
596
|
+
.highlighted {
|
|
597
|
+
background-color: var(--dpicker-selectedDayBgColor);
|
|
598
|
+
color: var(--dpicker-selectedDayTextColor);
|
|
599
|
+
opacity: 1 !important; /* Ensure full opacity for highlighted dates */
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
.other-month:not(.highlighted) {
|
|
603
|
+
color: var(--dpicker-textColor);
|
|
604
|
+
opacity: 0.65;
|
|
605
|
+
}
|
|
606
|
+
.calendar-day.blocked {
|
|
607
|
+
text-decoration: line-through;
|
|
608
|
+
color: var(--dpicker-inactiveDayColor);
|
|
609
|
+
cursor: not-allowed;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
.calendar-day.blocked:hover:not(:disabled) {
|
|
613
|
+
border: none;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
.calendar-day:disabled {
|
|
617
|
+
color: var(--dpicker-inactiveDayColor);
|
|
618
|
+
opacity: 0.7;
|
|
619
|
+
cursor: not-allowed;
|
|
620
|
+
}
|
|
621
|
+
.disabled-input {
|
|
622
|
+
opacity: 0.5;
|
|
623
|
+
cursor: not-allowed;
|
|
624
|
+
}
|
|
625
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
title: string;
|
|
3
|
+
name: string;
|
|
4
|
+
dateText?: string;
|
|
5
|
+
placeholder?: string;
|
|
6
|
+
minDate?: Date;
|
|
7
|
+
maxDate?: Date | null;
|
|
8
|
+
blockedDates?: Date[];
|
|
9
|
+
radius?: 'right' | 'left' | 'full';
|
|
10
|
+
position?: 'left-0' | 'right-0';
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
colorScheme?: Record<string, string>;
|
|
13
|
+
onDateUpdate?: (date: string) => void;
|
|
14
|
+
};
|
|
15
|
+
declare const DatePicker5: import("svelte").Component<$$ComponentProps, {}, "dateText">;
|
|
16
|
+
type DatePicker5 = ReturnType<typeof DatePicker5>;
|
|
17
|
+
export default DatePicker5;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@djcali570/component-lib",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"dev": "vite dev",
|
|
6
|
+
"build": "vite build && npm run prepack",
|
|
7
|
+
"package": "svelte-kit sync && svelte-package && publint",
|
|
8
|
+
"preview": "vite preview",
|
|
9
|
+
"prepare": "svelte-kit sync || echo ''",
|
|
10
|
+
"prepack": "svelte-kit sync && svelte-package && publint",
|
|
11
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
12
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
13
|
+
"format": "prettier --write .",
|
|
14
|
+
"lint": "prettier --check . && eslint ."
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"!dist/**/*.test.*",
|
|
19
|
+
"!dist/**/*.spec.*"
|
|
20
|
+
],
|
|
21
|
+
"sideEffects": [
|
|
22
|
+
"**/*.css"
|
|
23
|
+
],
|
|
24
|
+
"svelte": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"svelte": "./dist/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"svelte": "^5.0.0",
|
|
35
|
+
"@sveltejs/kit": "^2.16.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@eslint/compat": "^1.2.5",
|
|
39
|
+
"@eslint/js": "^9.18.0",
|
|
40
|
+
"@sveltejs/adapter-auto": "^6.0.0",
|
|
41
|
+
"@sveltejs/kit": "^2.16.0",
|
|
42
|
+
"@sveltejs/package": "^2.0.0",
|
|
43
|
+
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
|
44
|
+
"@tailwindcss/vite": "^4.0.0",
|
|
45
|
+
"eslint": "^9.18.0",
|
|
46
|
+
"eslint-config-prettier": "^10.0.1",
|
|
47
|
+
"eslint-plugin-svelte": "^3.0.0",
|
|
48
|
+
"globals": "^16.0.0",
|
|
49
|
+
"prettier": "^3.4.2",
|
|
50
|
+
"prettier-plugin-svelte": "^3.3.3",
|
|
51
|
+
"prettier-plugin-tailwindcss": "^0.6.11",
|
|
52
|
+
"publint": "^0.3.2",
|
|
53
|
+
"svelte": "^5.0.0",
|
|
54
|
+
"svelte-check": "^4.0.0",
|
|
55
|
+
"tailwindcss": "^4.0.0",
|
|
56
|
+
"typescript": "^5.0.0",
|
|
57
|
+
"typescript-eslint": "^8.20.0",
|
|
58
|
+
"vite": "^6.2.6"
|
|
59
|
+
},
|
|
60
|
+
"keywords": [
|
|
61
|
+
"svelte",
|
|
62
|
+
"SvelteKit",
|
|
63
|
+
"components"
|
|
64
|
+
],
|
|
65
|
+
"author": {
|
|
66
|
+
"name": "Jay Cali"
|
|
67
|
+
}
|
|
68
|
+
}
|