@lemonadejs/calendar 2.0.0 → 3.0.3
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 +81 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +790 -49
- package/dist/react.js +31 -0
- package/dist/style.css +231 -0
- package/dist/vue.js +32 -0
- package/package.json +23 -22
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# LemonadeJS Calendar
|
|
2
|
+
|
|
3
|
+
[Official website and documentation is here](https://lemonadejs.net/components/calendar)
|
|
4
|
+
|
|
5
|
+
Compatible with Vanilla JavaScript, LemonadeJS, React, Vue or Angular.
|
|
6
|
+
|
|
7
|
+
The LemonadeJS Calendar Component is a lightweight and agile calendar solution that empowers developers with efficient date management capabilities. With seamless navigation between months and years, intuitive day selection, and the ability to attach values and events, this highly customizable component provides a versatile foundation for scheduling applications, booking systems, and more. Its optimized codebase ensures fast performance, while its responsive design guarantees a consistent user experience across devices. Streamline your date management with the LemonadeJS Calendar Component and unlock enhanced productivity for your users.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Lightweight: The JavaScript Calendar is only about 2 KBytes;
|
|
12
|
+
- Integration: It can be used as a standalone library or integrated with any modern framework;
|
|
13
|
+
|
|
14
|
+
## Getting Started
|
|
15
|
+
|
|
16
|
+
You can install using NPM or using directly from a CDN.
|
|
17
|
+
|
|
18
|
+
### npm Installation
|
|
19
|
+
|
|
20
|
+
To install it in your project using npm, run the following command:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
$ npm install @lemonadejs/calendar
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### CDN
|
|
27
|
+
|
|
28
|
+
To use calendar via a CDN, include the following script tags in your HTML file:
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
|
|
32
|
+
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@lemonadejs/calendar/dist/index.min.js"></script>
|
|
33
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/calendar/dist/style.min.css" />
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Usage
|
|
37
|
+
|
|
38
|
+
Quick example with Lemonade
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
import Calendar from "@lemonadejs/calendar";
|
|
42
|
+
import "@lemonadejs/calendar/dist/style.css"
|
|
43
|
+
|
|
44
|
+
export default function App() {
|
|
45
|
+
const self = this;
|
|
46
|
+
|
|
47
|
+
return `<Calendar />`;
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Configuration
|
|
52
|
+
|
|
53
|
+
You can configure things such as calendar starting date, calendar events, and customize functions.
|
|
54
|
+
|
|
55
|
+
#### Calendar Properties
|
|
56
|
+
|
|
57
|
+
| Property | Type | Description |
|
|
58
|
+
| -------- | ---- | ----------- |
|
|
59
|
+
| value | date | The value currently attached to the calendar. |
|
|
60
|
+
| range | array | Defines a restricted range of selectable dates within the calendar. Example: ['2023-06-20', '2023-06-25']. |
|
|
61
|
+
| closed | boolean | Control when the calendar modal is open or closed. |
|
|
62
|
+
| time | boolean | Enables time selection into the calendar. |
|
|
63
|
+
| type | string | |
|
|
64
|
+
|
|
65
|
+
### Calendar Events
|
|
66
|
+
|
|
67
|
+
| Event | Type | Description |
|
|
68
|
+
| -------- | ---- | ----------- |
|
|
69
|
+
| onopen? | () => void | Called when modal opens. |
|
|
70
|
+
| onclose? | () => void | Called when modal closes. |
|
|
71
|
+
| onupdate? | (instance.value) => void | Called when value updates. |
|
|
72
|
+
| onchange? | (instance.value) => void | Called when some state inside the component changes. |
|
|
73
|
+
|
|
74
|
+
## License
|
|
75
|
+
|
|
76
|
+
The [LemonadeJS](https://lemonadejs.net) Calendar is released under the MIT.
|
|
77
|
+
|
|
78
|
+
## Other Tools
|
|
79
|
+
|
|
80
|
+
- [jSuites](https://jsuites.net/v4/)
|
|
81
|
+
- [Jspreadsheet](https://jspreadsheet.com)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Official Type definitions for LemonadeJS plugins
|
|
3
|
+
* https://lemonadejs.net
|
|
4
|
+
* Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
interface Calendar {
|
|
8
|
+
(): any
|
|
9
|
+
[key: string]: any
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface options {
|
|
13
|
+
range?: boolean;
|
|
14
|
+
type?: 'default' | 'inline';
|
|
15
|
+
value?: number | string;
|
|
16
|
+
numeric?: boolean;
|
|
17
|
+
input?: HTMLElement;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface instance {
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export declare function Calendar(el: HTMLElement, options?: options): instance;
|
package/dist/index.js
CHANGED
|
@@ -1,50 +1,791 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
1
|
+
/**
|
|
2
|
+
* render: ()
|
|
3
|
+
* valid-ranges: []
|
|
4
|
+
* disabled
|
|
5
|
+
* dateToNum UTC
|
|
6
|
+
* navigation with icons Enter key
|
|
7
|
+
*/
|
|
8
|
+
if (! lemonade && typeof (require) === 'function') {
|
|
9
|
+
var lemonade = require('lemonadejs');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (! Modal && typeof (require) === 'function') {
|
|
13
|
+
var Modal = require('@lemonadejs/modal');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
; (function (global, factory) {
|
|
17
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
18
|
+
typeof define === 'function' && define.amd ? define(factory) :
|
|
19
|
+
global.Calendar = factory();
|
|
20
|
+
}(this, (function () {
|
|
21
|
+
|
|
22
|
+
// Weekdays
|
|
23
|
+
const Weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
|
24
|
+
|
|
25
|
+
// Months
|
|
26
|
+
const Months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
|
27
|
+
|
|
28
|
+
// Excel like dates
|
|
29
|
+
const excelInitialTime = Date.UTC(1900, 0, 0);
|
|
30
|
+
const excelLeapYearBug = Date.UTC(1900, 1, 29);
|
|
31
|
+
const millisecondsPerDay = 86400000;
|
|
32
|
+
|
|
33
|
+
function isValidDate(d) {
|
|
34
|
+
return d instanceof Date && !isNaN(d.getTime());
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Date to number
|
|
39
|
+
*/
|
|
40
|
+
const dateToNum = function (jsDate) {
|
|
41
|
+
if (typeof(jsDate) === 'string') {
|
|
42
|
+
jsDate = new Date(jsDate + ' GMT+0');
|
|
43
|
+
}
|
|
44
|
+
let jsDateInMilliseconds = jsDate.getTime();
|
|
45
|
+
if (jsDateInMilliseconds >= excelLeapYearBug) {
|
|
46
|
+
jsDateInMilliseconds += millisecondsPerDay;
|
|
47
|
+
}
|
|
48
|
+
jsDateInMilliseconds -= excelInitialTime;
|
|
49
|
+
|
|
50
|
+
return jsDateInMilliseconds / millisecondsPerDay;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const numToDate = function (excelSerialNumber, asString) {
|
|
54
|
+
if (! excelSerialNumber) {
|
|
55
|
+
return '';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let jsDateInMilliseconds = excelInitialTime + excelSerialNumber * millisecondsPerDay;
|
|
59
|
+
if (jsDateInMilliseconds >= excelLeapYearBug) {
|
|
60
|
+
jsDateInMilliseconds -= millisecondsPerDay;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const d = new Date(jsDateInMilliseconds);
|
|
64
|
+
|
|
65
|
+
let arr = [
|
|
66
|
+
d.getUTCFullYear(),
|
|
67
|
+
Two(d.getUTCMonth() + 1),
|
|
68
|
+
Two(d.getUTCDate()),
|
|
69
|
+
Two(d.getUTCHours()),
|
|
70
|
+
Two(d.getUTCMinutes()),
|
|
71
|
+
Two(d.getUTCSeconds()),
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
if (asString) {
|
|
75
|
+
return arrayToStringDate(arr);
|
|
76
|
+
} else {
|
|
77
|
+
return arr;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const arrayToStringDate = function(arr) {
|
|
82
|
+
return [arr[0],Two(arr[1]),Two(arr[2])].join('-');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Create a data calendar object based on the view
|
|
87
|
+
*/
|
|
88
|
+
const views = {
|
|
89
|
+
years: function(date) {
|
|
90
|
+
let year = date.getFullYear();
|
|
91
|
+
let result = [];
|
|
92
|
+
let start = year % 16;
|
|
93
|
+
let complement = 16 - start;
|
|
94
|
+
|
|
95
|
+
for (let i = year-start; i < year+complement; i++) {
|
|
96
|
+
let item = {
|
|
97
|
+
title: i,
|
|
98
|
+
value: i
|
|
99
|
+
};
|
|
100
|
+
result.push(item);
|
|
101
|
+
// Select cursor
|
|
102
|
+
if (this.cursor.y === i) {
|
|
103
|
+
// Select item
|
|
104
|
+
item.selected = true;
|
|
105
|
+
// Cursor
|
|
106
|
+
this.cursor.index = result.length - 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
},
|
|
111
|
+
months: function(date) {
|
|
112
|
+
let year = date.getFullYear();
|
|
113
|
+
let result = [];
|
|
114
|
+
for (let i = 0; i < 12; i++) {
|
|
115
|
+
let item = {
|
|
116
|
+
title: Months[i].substring(0,3),
|
|
117
|
+
value: i
|
|
118
|
+
}
|
|
119
|
+
// Add the item to the data
|
|
120
|
+
result.push(item);
|
|
121
|
+
// Select cursor
|
|
122
|
+
if (this.cursor.y === year && this.cursor.m === i) {
|
|
123
|
+
// Select item
|
|
124
|
+
item.selected = true;
|
|
125
|
+
// Cursor
|
|
126
|
+
this.cursor.index = result.length - 1;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return result;
|
|
131
|
+
},
|
|
132
|
+
days: function(date) {
|
|
133
|
+
let year = date.getFullYear();
|
|
134
|
+
let month = date.getMonth();
|
|
135
|
+
let data = filterData.call(this, year, month);
|
|
136
|
+
|
|
137
|
+
// First day
|
|
138
|
+
let tmp = new Date(year, month, 1, 0, 0, 0);
|
|
139
|
+
let firstDay = tmp.getDay();
|
|
140
|
+
|
|
141
|
+
let result = [];
|
|
142
|
+
for (let i = 1-firstDay; i <= 42-firstDay; i++) {
|
|
143
|
+
// Get the day
|
|
144
|
+
tmp = new Date(year, month, i, 0, 0, 0);
|
|
145
|
+
// Day
|
|
146
|
+
let day = tmp.getDate();
|
|
147
|
+
// Create the item
|
|
148
|
+
let item = {
|
|
149
|
+
title: day,
|
|
150
|
+
value: i,
|
|
151
|
+
number: dateToNum(tmp.toString())
|
|
152
|
+
}
|
|
153
|
+
// Add the item to the date
|
|
154
|
+
result.push(item);
|
|
155
|
+
// Check selections
|
|
156
|
+
if (tmp.getMonth() !== month) {
|
|
157
|
+
// Days are not in the current month
|
|
158
|
+
item.grey = true;
|
|
159
|
+
} else {
|
|
160
|
+
// Check for data
|
|
161
|
+
let d = [ year, Two(month+1), Two(day)].join('-');
|
|
162
|
+
if (data && data[d]) {
|
|
163
|
+
item.data = data[d];
|
|
164
|
+
}
|
|
165
|
+
// Select cursor
|
|
166
|
+
if (this.cursor.y === year && this.cursor.m === month && this.cursor.d === day) {
|
|
167
|
+
// Select item
|
|
168
|
+
item.selected = true;
|
|
169
|
+
// Cursor
|
|
170
|
+
this.cursor.index = result.length - 1;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Select range
|
|
175
|
+
if (this.range && this.rangeValues) {
|
|
176
|
+
// Mark the start and end points
|
|
177
|
+
if (this.rangeValues[0] === item.number) {
|
|
178
|
+
item.range = true;
|
|
179
|
+
item.start = true;
|
|
180
|
+
}
|
|
181
|
+
if (this.rangeValues[1] === item.number) {
|
|
182
|
+
item.range = true;
|
|
183
|
+
item.end = true;
|
|
184
|
+
}
|
|
185
|
+
// Re-recreate teh range
|
|
186
|
+
if (this.rangeValues[0] && this.rangeValues[1]) {
|
|
187
|
+
if (this.rangeValues[0] <= item.number && this.rangeValues[1] >= item.number) {
|
|
188
|
+
item.range = true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return result;
|
|
195
|
+
},
|
|
196
|
+
hours: function() {
|
|
197
|
+
let result = [];
|
|
198
|
+
for (let i = 0; i < 24; i++) {
|
|
199
|
+
let item = {
|
|
200
|
+
title: Two(i),
|
|
201
|
+
value: i
|
|
202
|
+
};
|
|
203
|
+
result.push(item);
|
|
204
|
+
}
|
|
205
|
+
return result;
|
|
206
|
+
},
|
|
207
|
+
minutes: function() {
|
|
208
|
+
let result = [];
|
|
209
|
+
for (let i = 0; i < 60; i=i+5) {
|
|
210
|
+
let item = {
|
|
211
|
+
title: Two(i),
|
|
212
|
+
value: i
|
|
213
|
+
};
|
|
214
|
+
result.push(item);
|
|
215
|
+
}
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const filterData = function(year, month) {
|
|
221
|
+
// Data for the month
|
|
222
|
+
let data = {};
|
|
223
|
+
if (Array.isArray(this.data)) {
|
|
224
|
+
this.data.map(function (v) {
|
|
225
|
+
let d = year + '-' + Two(month + 1);
|
|
226
|
+
if (v.date.substring(0, 7) === d) {
|
|
227
|
+
if (!data[v.date]) {
|
|
228
|
+
data[v.date] = [];
|
|
229
|
+
}
|
|
230
|
+
data[v.date].push(v);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
return data;
|
|
235
|
+
}
|
|
236
|
+
// Get the short weekdays name
|
|
237
|
+
const getWeekdays = function() {
|
|
238
|
+
return Weekdays.map(w => {
|
|
239
|
+
return { title: w.substring(0, 1) };
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Define the hump based on the view
|
|
244
|
+
const getJump = function(e) {
|
|
245
|
+
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
|
|
246
|
+
return this.view === 'days' ? 7 : 4;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return 1;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Get the position of the data based on the view
|
|
253
|
+
const getPosition = function() {
|
|
254
|
+
let position = 2;
|
|
255
|
+
if (this.view === 'years') {
|
|
256
|
+
position = 0;
|
|
257
|
+
} else if (this.view === 'months') {
|
|
258
|
+
position = 1;
|
|
259
|
+
}
|
|
260
|
+
return position;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Transform in two digits
|
|
264
|
+
const Two = function(value) {
|
|
265
|
+
value = '' + value;
|
|
266
|
+
if (value.length === 1) {
|
|
267
|
+
value = '0' + value;
|
|
268
|
+
}
|
|
269
|
+
return value;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const Calendar = function() {
|
|
273
|
+
let self = this;
|
|
274
|
+
|
|
275
|
+
// Weekdays
|
|
276
|
+
self.weekdays = getWeekdays();
|
|
277
|
+
|
|
278
|
+
// Cursor
|
|
279
|
+
self.cursor = {};
|
|
280
|
+
|
|
281
|
+
// Calendar date
|
|
282
|
+
let date = new Date();
|
|
283
|
+
|
|
284
|
+
// Range
|
|
285
|
+
self.rangeValues = null;
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Update the internal date
|
|
289
|
+
* @param {Date|string|number[]} d instance of Date
|
|
290
|
+
*
|
|
291
|
+
*/
|
|
292
|
+
const setDate = function(d) {
|
|
293
|
+
if (Array.isArray(d)) {
|
|
294
|
+
d = new Date(Date.UTC(...d));
|
|
295
|
+
} else if (typeof(d) === 'string') {
|
|
296
|
+
d = new Date(d);
|
|
297
|
+
}
|
|
298
|
+
// Update internal date
|
|
299
|
+
date = d;
|
|
300
|
+
// Update the headers of the calendar
|
|
301
|
+
let value = d.toISOString().substring(0,10).split('-');
|
|
302
|
+
// Update the month label
|
|
303
|
+
self.month = Months[parseInt(value[1])-1];
|
|
304
|
+
// Update the year label
|
|
305
|
+
self.year = parseInt(value[0]);
|
|
306
|
+
// Load data
|
|
307
|
+
if (! self.view) {
|
|
308
|
+
// Start on the days view will start the data
|
|
309
|
+
self.view = 'days';
|
|
310
|
+
} else {
|
|
311
|
+
// Reload the data for the same view
|
|
312
|
+
self.options = views[self.view].call(self, date);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Set the internal cursor
|
|
318
|
+
* @param {object} s
|
|
319
|
+
*/
|
|
320
|
+
const setCursor = function(s) {
|
|
321
|
+
// Remove selection from the current object
|
|
322
|
+
let item = self.options[self.cursor.index];
|
|
323
|
+
if (typeof(item) !== 'undefined') {
|
|
324
|
+
item.selected = false;
|
|
325
|
+
}
|
|
326
|
+
// Update the date based on the click
|
|
327
|
+
let v = updateDate(s.value, getPosition.call(self));
|
|
328
|
+
let d = new Date(Date.UTC(...v));
|
|
329
|
+
// Update cursor controller
|
|
330
|
+
self.cursor = {
|
|
331
|
+
y: d.getFullYear(),
|
|
332
|
+
m: d.getMonth(),
|
|
333
|
+
d: d.getDate(),
|
|
334
|
+
};
|
|
335
|
+
// Update cursor based on the object position
|
|
336
|
+
if (s) {
|
|
337
|
+
// Update selected property
|
|
338
|
+
s.selected = true;
|
|
339
|
+
// New cursor
|
|
340
|
+
self.cursor.index = self.options.indexOf(s);
|
|
341
|
+
}
|
|
342
|
+
return d;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Update the current date
|
|
347
|
+
* @param {number} v new value for year, month or day
|
|
348
|
+
* @param {number} position (0,1,2 - year,month,day)
|
|
349
|
+
* @returns {number[]}
|
|
350
|
+
*/
|
|
351
|
+
const updateDate = function(v, position) {
|
|
352
|
+
// Current internal date
|
|
353
|
+
let value = [date.getFullYear(), date.getMonth(), date.getDate(),0,0,0];
|
|
354
|
+
// Update internal date
|
|
355
|
+
value[position] = v;
|
|
356
|
+
// Return new value
|
|
357
|
+
return value;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* This method move the data from the view up or down
|
|
362
|
+
* @param direction
|
|
363
|
+
*/
|
|
364
|
+
const move = function(direction) {
|
|
365
|
+
let value;
|
|
366
|
+
|
|
367
|
+
// Update the new internal date
|
|
368
|
+
if (self.view === 'days') {
|
|
369
|
+
// Select the new internal date
|
|
370
|
+
value = updateDate(date.getMonth()+direction, 1);
|
|
371
|
+
} else if (self.view === 'months') {
|
|
372
|
+
// Select the new internal date
|
|
373
|
+
value = updateDate(date.getFullYear()+direction, 0);
|
|
374
|
+
} else if (self.view === 'years') {
|
|
375
|
+
// Select the new internal date
|
|
376
|
+
value = updateDate(date.getFullYear()+(direction*16), 0);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Update view
|
|
380
|
+
if (value) {
|
|
381
|
+
setDate(value);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Keyboard handler
|
|
387
|
+
* @param {number} direction of the action
|
|
388
|
+
* @param {object} e keyboard event
|
|
389
|
+
*/
|
|
390
|
+
const moveCursor = function(direction, e) {
|
|
391
|
+
direction = direction * getJump.call(self, e);
|
|
392
|
+
// Remove the selected from the current selection
|
|
393
|
+
let s = self.options[self.cursor.index];
|
|
394
|
+
// If the selection is going outside the viewport
|
|
395
|
+
if (typeof(s) === 'undefined' || ! s.selected) {
|
|
396
|
+
// Go back to the view
|
|
397
|
+
setDate([ self.cursor.y, self.cursor.m, self.cursor.d ]);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Jump to the index
|
|
401
|
+
let index = self.cursor.index + direction;
|
|
402
|
+
|
|
403
|
+
// See if the new position is in the viewport
|
|
404
|
+
if (typeof(self.options[index]) === 'undefined') {
|
|
405
|
+
// Adjust the index for next collection of data
|
|
406
|
+
if (self.view === 'days') {
|
|
407
|
+
if (index < 0) {
|
|
408
|
+
index = 42 + index;
|
|
409
|
+
} else {
|
|
410
|
+
index = index - 42;
|
|
411
|
+
}
|
|
412
|
+
} else if (self.view === 'years') {
|
|
413
|
+
if (index < 0) {
|
|
414
|
+
index = 4 + index;
|
|
415
|
+
} else {
|
|
416
|
+
index = index - 4;
|
|
417
|
+
}
|
|
418
|
+
} else if (self.view === 'months') {
|
|
419
|
+
if (index < 0) {
|
|
420
|
+
index = 12 + index;
|
|
421
|
+
} else {
|
|
422
|
+
index = index - 12;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Move the data up or down
|
|
427
|
+
move(direction > 0 ? 1 : -1);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Update the date based on the click
|
|
431
|
+
setCursor(self.options[index]);
|
|
432
|
+
|
|
433
|
+
// Update ranges
|
|
434
|
+
updateRange(self.options[index])
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Handler blur
|
|
439
|
+
* @param e
|
|
440
|
+
*/
|
|
441
|
+
const blur = function(e) {
|
|
442
|
+
if (self.modal) {
|
|
443
|
+
if (!(e.relatedTarget && self.modal.el.contains(e.relatedTarget))) {
|
|
444
|
+
if (self.modal.closed === false) {
|
|
445
|
+
self.modal.closed = true
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Set the limits of a range
|
|
453
|
+
* @param s
|
|
454
|
+
*/
|
|
455
|
+
const setRange = function(s) {
|
|
456
|
+
if (self.view === 'days' && self.range) {
|
|
457
|
+
let d = self.getValue();
|
|
458
|
+
// Date to number
|
|
459
|
+
let number = dateToNum(d);
|
|
460
|
+
// Start a new range
|
|
461
|
+
if (self.rangeValues && (self.rangeValues[0] > number || self.rangeValues[1])) {
|
|
462
|
+
destroyRange();
|
|
463
|
+
}
|
|
464
|
+
// Range
|
|
465
|
+
s.range = true;
|
|
466
|
+
// Update range
|
|
467
|
+
if (! self.rangeValues) {
|
|
468
|
+
s.start = true;
|
|
469
|
+
self.rangeValues = [number, null];
|
|
470
|
+
} else {
|
|
471
|
+
s.end = true;
|
|
472
|
+
self.rangeValues[1] = number;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Update the visible range
|
|
479
|
+
* @param s
|
|
480
|
+
*/
|
|
481
|
+
const updateRange = function(s) {
|
|
482
|
+
if (self.range && self.view === 'days' && self.rangeValues) {
|
|
483
|
+
// Creating a range
|
|
484
|
+
if (self.rangeValues[0] && ! self.rangeValues[1]) {
|
|
485
|
+
let number = s.number;
|
|
486
|
+
if (number) {
|
|
487
|
+
// Update range properties
|
|
488
|
+
for (let i = 0; i < self.options.length; i++) {
|
|
489
|
+
// Item number
|
|
490
|
+
let v = self.options[i].number;
|
|
491
|
+
// Update property condition
|
|
492
|
+
self.options[i].range = v >= self.rangeValues[0] && v <= number;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Destroy the range
|
|
501
|
+
*/
|
|
502
|
+
const destroyRange = function() {
|
|
503
|
+
for (let i = 0; i < self.options.length; i++) {
|
|
504
|
+
self.options[i].range = false;
|
|
505
|
+
self.options[i].start = false;
|
|
506
|
+
self.options[i].end = false;
|
|
507
|
+
}
|
|
508
|
+
self.rangeValues = null;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const setValue = function(reset) {
|
|
512
|
+
if (reset) {
|
|
513
|
+
self.value = '';
|
|
514
|
+
self.input.value = '';
|
|
515
|
+
destroyRange();
|
|
516
|
+
} else {
|
|
517
|
+
if (self.range) {
|
|
518
|
+
self.value = self.rangeValues[0];
|
|
519
|
+
if (self.input) {
|
|
520
|
+
if (self.numeric) {
|
|
521
|
+
self.input.value = self.rangeValues;
|
|
522
|
+
} else {
|
|
523
|
+
self.input.value = [numToDate(self.rangeValues[0], true), numToDate(self.rangeValues[1], true)];
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
} else {
|
|
527
|
+
self.value = self.getValue();
|
|
528
|
+
if (self.input) {
|
|
529
|
+
if (self.numeric) {
|
|
530
|
+
self.input.value = dateToNum(self.value);
|
|
531
|
+
} else {
|
|
532
|
+
self.input.value = self.value;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Select an item with the enter or mouse
|
|
541
|
+
* @param {object} e - mouse event
|
|
542
|
+
* @param {object} item - selected cell
|
|
543
|
+
*/
|
|
544
|
+
self.select = function(e, item) {
|
|
545
|
+
// Update cursor generic
|
|
546
|
+
let value = setCursor(item);
|
|
547
|
+
// Update range
|
|
548
|
+
setRange(item);
|
|
549
|
+
// Based where was the click
|
|
550
|
+
if (! (self.view === 'days' && ! item.grey)) {
|
|
551
|
+
// Update the internal date
|
|
552
|
+
setDate(value);
|
|
553
|
+
}
|
|
554
|
+
// Go back to the view of days
|
|
555
|
+
if (self.view !== 'days') {
|
|
556
|
+
self.view = 'days';
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Next handler
|
|
562
|
+
* @param {object?} e mouse event
|
|
563
|
+
*/
|
|
564
|
+
self.next = function(e) {
|
|
565
|
+
if (! e || e.type === 'click') {
|
|
566
|
+
// Icon click
|
|
567
|
+
move(1);
|
|
568
|
+
} else {
|
|
569
|
+
// Keyboard handler
|
|
570
|
+
moveCursor(1, e);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Next handler
|
|
576
|
+
* @param {object?} e mouse event
|
|
577
|
+
*/
|
|
578
|
+
self.prev = function(e) {
|
|
579
|
+
if (! e || e.type === 'click') {
|
|
580
|
+
// Icon click
|
|
581
|
+
move(-1);
|
|
582
|
+
} else {
|
|
583
|
+
// Keyboard handler
|
|
584
|
+
moveCursor(-1, e);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Open the modal
|
|
590
|
+
*/
|
|
591
|
+
self.open = function(e) {
|
|
592
|
+
if (self.modal && self.modal.closed) {
|
|
593
|
+
// Open modal
|
|
594
|
+
self.modal.closed = false;
|
|
595
|
+
// Set the focus on the content to use the keyboard
|
|
596
|
+
if (! (self.input && e.target.getAttribute('readonly') === null)) {
|
|
597
|
+
self.content.focus();
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Close the modal
|
|
604
|
+
*/
|
|
605
|
+
self.close = function() {
|
|
606
|
+
if (self.modal && self.modal.closed === false) {
|
|
607
|
+
// Close modal
|
|
608
|
+
self.modal.closed = true;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
self.reset = function() {
|
|
613
|
+
setValue(true);
|
|
614
|
+
self.close();
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
self.update = function() {
|
|
618
|
+
setValue();
|
|
619
|
+
self.close();
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Change the view
|
|
624
|
+
*/
|
|
625
|
+
self.setView = function() {
|
|
626
|
+
let v = this.getAttribute('data-view');
|
|
627
|
+
if (v) {
|
|
628
|
+
self.view = v;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Get value from cursor
|
|
634
|
+
* @returns {string}
|
|
635
|
+
*/
|
|
636
|
+
self.getValue = function() {
|
|
637
|
+
let v = [ self.cursor.y, self.cursor.m, self.cursor.d ];
|
|
638
|
+
let d = new Date(Date.UTC(...v));
|
|
639
|
+
// Update the headers of the calendar
|
|
640
|
+
return d.toISOString().substring(0,10);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
self.onchange = function(prop) {
|
|
644
|
+
if (prop === 'view') {
|
|
645
|
+
if (typeof(views[self.view]) === 'function') {
|
|
646
|
+
// When change the view update the data
|
|
647
|
+
self.options = views[self.view].call(self, date);
|
|
648
|
+
}
|
|
649
|
+
} else if (prop === 'value') {
|
|
650
|
+
if (typeof(self.onupdate) === 'function') {
|
|
651
|
+
self.onupdate.call(self, self.value);
|
|
652
|
+
}
|
|
653
|
+
} else if (prop === 'options') {
|
|
654
|
+
self.content.focus();
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
self.onload = function() {
|
|
659
|
+
let d;
|
|
660
|
+
if (self.value) {
|
|
661
|
+
d = new Date(self.value);
|
|
662
|
+
}
|
|
663
|
+
// if no date is defined
|
|
664
|
+
if (! isValidDate(d)) {
|
|
665
|
+
d = new Date();
|
|
666
|
+
}
|
|
667
|
+
// Update my index
|
|
668
|
+
self.cursor = {
|
|
669
|
+
y: d.getFullYear(),
|
|
670
|
+
m: d.getMonth(),
|
|
671
|
+
d: d.getDate(),
|
|
672
|
+
};
|
|
673
|
+
// Populate components
|
|
674
|
+
self.hours = views.hours();
|
|
675
|
+
self.minutes = views.minutes();
|
|
676
|
+
|
|
677
|
+
// Update the internal calendar date
|
|
678
|
+
setDate(d);
|
|
679
|
+
|
|
680
|
+
if (self.type !== "inline") {
|
|
681
|
+
// Create modal instance
|
|
682
|
+
self.modal = {
|
|
683
|
+
width: 300,
|
|
684
|
+
closed: true,
|
|
685
|
+
focus: false,
|
|
686
|
+
};
|
|
687
|
+
// Generate modal
|
|
688
|
+
Modal(self.el, self.modal);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// Create input controls
|
|
692
|
+
if (self.input) {
|
|
693
|
+
self.input.classList.add('lm-calendar-input');
|
|
694
|
+
self.input.addEventListener('focus', self.open);
|
|
695
|
+
self.input.addEventListener('click', self.open);
|
|
696
|
+
self.input.addEventListener('blur', blur);
|
|
697
|
+
|
|
698
|
+
// Retrieve the value
|
|
699
|
+
if (self.value) {
|
|
700
|
+
self.input.value = self.value;
|
|
701
|
+
} else if (self.input.value) {
|
|
702
|
+
self.value = self.input.value;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Handler keyboard
|
|
708
|
+
* @param {object} e - event
|
|
709
|
+
*/
|
|
710
|
+
self.content.addEventListener('keydown', function(e){
|
|
711
|
+
if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') {
|
|
712
|
+
self.prev(e);
|
|
713
|
+
} else if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
|
|
714
|
+
self.next(e);
|
|
715
|
+
} else if (e.key === 'Enter') {
|
|
716
|
+
// Current view
|
|
717
|
+
let view = self.view;
|
|
718
|
+
// Select
|
|
719
|
+
self.select(e, self.options[self.cursor.index]);
|
|
720
|
+
// If is range do something diferent
|
|
721
|
+
if (view === 'days' && ! self.range) {
|
|
722
|
+
self.update();
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Mouse wheel handler
|
|
729
|
+
* @param {object} e - mouse event
|
|
730
|
+
*/
|
|
731
|
+
self.content.addEventListener('wheel', function(e){
|
|
732
|
+
if (e.deltaY < 0) {
|
|
733
|
+
self.prev();
|
|
734
|
+
} else {
|
|
735
|
+
self.next();
|
|
736
|
+
}
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Range handler
|
|
741
|
+
* @param {object} e - mouse event
|
|
742
|
+
*/
|
|
743
|
+
self.content.addEventListener('mouseover', function(e){
|
|
744
|
+
if (e.target.lemon) {
|
|
745
|
+
updateRange(e.target.lemon.self);
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
// Create event for focus out
|
|
750
|
+
self.el.addEventListener("focusout", (e) => {
|
|
751
|
+
if (e.relatedTarget !== self.input && ! self.el.contains(e.relatedTarget)) {
|
|
752
|
+
self.close();
|
|
753
|
+
}
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
return `<div class="lm-calendar">
|
|
758
|
+
<div class="lm-calendar-options">
|
|
759
|
+
<button onclick="self.reset">Reset</button>
|
|
760
|
+
<button onclick="self.update">Done</button>
|
|
761
|
+
</div>
|
|
762
|
+
<div class="lm-calendar-container" data-view="{{self.view}}">
|
|
763
|
+
<div class="lm-calendar-header">
|
|
764
|
+
<div>
|
|
765
|
+
<div class="lm-calendar-labels"><button onclick="self.setView" data-view="months">{{self.month}}</button> <button onclick="self.setView" data-view="years">{{self.year}}</button></div>
|
|
766
|
+
<div class="lm-calendar-navigation"><button type="button" class="material-icons ripple" onclick="self.prev" tabindex="0">arrow_drop_up</button> <button type="button" class="material-icons ripple" onclick="self.next" tabindex="0">arrow_drop_down</button></div>
|
|
767
|
+
</div>
|
|
768
|
+
<div class="lm-calendar-weekdays" :loop="self.weekdays"><div>{{self.title}}</div></div>
|
|
769
|
+
</div>
|
|
770
|
+
<div class="lm-calendar-content" :loop="self.options" tabindex="0" :ref="self.content">
|
|
771
|
+
<div data-start="{{self.start}}" data-end="{{self.end}}" data-range="{{self.range}}" data-event="{{self.data}}" data-grey="{{self.grey}}" data-bold="{{self.bold}}" data-selected="{{self.selected}}" onclick="self.parent.select">{{self.title}}</div>
|
|
772
|
+
</div>
|
|
773
|
+
<div class="lm-calendar-footer">
|
|
774
|
+
<div><select :loop="self.hours"><option value="{{self.value}}">{{self.title}}</option></select>:<select :loop="self.minutes"><option value="{{self.value}}">{{self.title}}</option></select></div>
|
|
775
|
+
<div><input type="button" value="Update" onclick="self.update" class="ripple"></div>
|
|
776
|
+
</div>
|
|
777
|
+
</div>
|
|
778
|
+
</div>`
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
lemonade.setComponents({ Calendar: Calendar });
|
|
782
|
+
|
|
783
|
+
return function (root, options) {
|
|
784
|
+
if (typeof (root) === 'object') {
|
|
785
|
+
lemonade.render(Calendar, root, options)
|
|
786
|
+
return options;
|
|
787
|
+
} else {
|
|
788
|
+
return Calendar.call(this, root)
|
|
789
|
+
}
|
|
790
|
+
}
|
|
50
791
|
})));
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import React, { useRef, useEffect } from "react";
|
|
3
|
+
import Component from './index';
|
|
4
|
+
|
|
5
|
+
import "./style.css";
|
|
6
|
+
import "@lemonadejs/modal/dist/style.css"
|
|
7
|
+
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
const Calendar = React.forwardRef((props, mainReference) => {
|
|
10
|
+
// Dom element
|
|
11
|
+
const Ref = useRef(null);
|
|
12
|
+
|
|
13
|
+
// Get the properties for the spreadsheet
|
|
14
|
+
let options = { ...props };
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
if (! Ref.current.innerHTML) {
|
|
19
|
+
mainReference.current = Component(Ref.current, options);
|
|
20
|
+
}
|
|
21
|
+
}, []);
|
|
22
|
+
|
|
23
|
+
let prop = {
|
|
24
|
+
ref: Ref,
|
|
25
|
+
style: { height: '100%', width: '100%' }
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return React.createElement("div", prop);
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
export default Calendar;
|
package/dist/style.css
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
.lm-calendar .lm-modal {
|
|
2
|
+
min-width: initial;
|
|
3
|
+
min-height: initial;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.lm-calendar-options {
|
|
7
|
+
display: none;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.lm-modal .lm-calendar-options {
|
|
11
|
+
display: flex;
|
|
12
|
+
justify-content: space-between;
|
|
13
|
+
border-bottom: 1px solid #ddd;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.lm-modal .lm-calendar-options button {
|
|
17
|
+
border: 0;
|
|
18
|
+
background-color: transparent;
|
|
19
|
+
text-transform: uppercase;
|
|
20
|
+
cursor: pointer;
|
|
21
|
+
padding: 15px;
|
|
22
|
+
font-weight: bold;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.lm-calendar-header {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.lm-calendar-header > div:first-child {
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
padding: 10px;
|
|
34
|
+
flex: 1;
|
|
35
|
+
user-select: none;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.lm-calendar-header .lm-calendar-labels {
|
|
39
|
+
display: flex;
|
|
40
|
+
flex: 1;
|
|
41
|
+
cursor: pointer;
|
|
42
|
+
padding-left: 5px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.lm-calendar-header .lm-calendar-labels > button {
|
|
46
|
+
font-size: 1.2em;
|
|
47
|
+
border: 0;
|
|
48
|
+
padding: 2px;
|
|
49
|
+
margin: 2px;
|
|
50
|
+
background-color: #fff;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.lm-calendar-navigation button {
|
|
54
|
+
cursor: pointer;
|
|
55
|
+
padding: 5px;
|
|
56
|
+
border: 0;
|
|
57
|
+
border-radius: 24px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.lm-calendar-navigation i:hover {
|
|
61
|
+
background-color: #eee;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.lm-calendar-weekdays {
|
|
65
|
+
display: none;
|
|
66
|
+
grid-template-columns: repeat(7, 1fr);
|
|
67
|
+
grid-gap: 2px;
|
|
68
|
+
padding: 0 10px 0 10px;
|
|
69
|
+
font-size: 0.8em;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.lm-calendar-container[data-view="days"] .lm-calendar-weekdays {
|
|
73
|
+
display: grid;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.lm-calendar-weekdays > div {
|
|
77
|
+
display: inline-block;
|
|
78
|
+
padding: 10px;
|
|
79
|
+
box-sizing: border-box;
|
|
80
|
+
text-align: center;
|
|
81
|
+
font-weight: bold;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.lm-calendar-content {
|
|
85
|
+
display: grid;
|
|
86
|
+
grid-template-columns: repeat(7, 1fr);
|
|
87
|
+
grid-gap: 0;
|
|
88
|
+
padding: 8px;
|
|
89
|
+
font-size: 0.8em;
|
|
90
|
+
outline: none;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.lm-calendar-content > div {
|
|
94
|
+
box-sizing: border-box;
|
|
95
|
+
text-align: center;
|
|
96
|
+
aspect-ratio: 1 / 1;
|
|
97
|
+
display: flex;
|
|
98
|
+
flex-direction: column;
|
|
99
|
+
justify-content: center;
|
|
100
|
+
align-items: center;
|
|
101
|
+
padding: 10px;
|
|
102
|
+
cursor: pointer;
|
|
103
|
+
border-radius: 100px;
|
|
104
|
+
background-origin: padding-box;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.lm-calendar-container[data-view="months"] .lm-calendar-content {
|
|
108
|
+
grid-template-columns: repeat(4, 1fr);
|
|
109
|
+
}
|
|
110
|
+
.lm-calendar-container[data-view="years"] .lm-calendar-content {
|
|
111
|
+
grid-template-columns: repeat(4, 1fr);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.lm-calendar-content > div[data-grey="true"] {
|
|
115
|
+
color: #ccc;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.lm-calendar-content > div[data-bold="true"] {
|
|
119
|
+
font-weight: bold;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.lm-calendar-content > div[data-event="true"]::before {
|
|
123
|
+
content: '';
|
|
124
|
+
position: absolute;
|
|
125
|
+
margin-top: 22px;
|
|
126
|
+
width: 3px;
|
|
127
|
+
height: 3px;
|
|
128
|
+
border-radius: 3px;
|
|
129
|
+
background-color: red;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.lm-calendar-content > div[data-selected="true"] {
|
|
133
|
+
font-weight: bold;
|
|
134
|
+
background-color: #eee;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.lm-calendar-content:focus > div[data-selected="true"] {
|
|
138
|
+
outline: 2px solid black;
|
|
139
|
+
outline-offset: -2px;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.lm-calendar-content > div:hover {
|
|
143
|
+
background-color: #eee;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.lm-calendar-content > div[data-range="true"] {
|
|
147
|
+
position: relative;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.lm-calendar-content > div[data-start="true"],
|
|
151
|
+
.lm-calendar-content > div[data-end="true"] {
|
|
152
|
+
background-color: #78D350;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.lm-calendar-content > div[data-range="true"]::before {
|
|
156
|
+
content: "";
|
|
157
|
+
position: absolute;
|
|
158
|
+
left: 0;
|
|
159
|
+
right: 0;
|
|
160
|
+
top: 8px;
|
|
161
|
+
bottom: 8px;
|
|
162
|
+
background-color: #78D35050;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.lm-calendar-content > div[data-start="true"]::before {
|
|
166
|
+
left: 32px;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.lm-calendar-content > div[data-end="true"]::before {
|
|
170
|
+
right: 32px;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.lm-calendar-footer > div {
|
|
174
|
+
flex: 1;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.lm-calendar-footer {
|
|
178
|
+
display: flex;
|
|
179
|
+
margin: 0 10px 0 10px;
|
|
180
|
+
padding: 8px 0 8px 0;
|
|
181
|
+
line-height: 34px;
|
|
182
|
+
border-top: 1px solid #eee;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.lm-calendar-footer select {
|
|
186
|
+
border: 0;
|
|
187
|
+
background-color: transparent;
|
|
188
|
+
padding: 6px;
|
|
189
|
+
-moz-appearance: none;
|
|
190
|
+
-webkit-appearance: none;
|
|
191
|
+
margin: 2px;
|
|
192
|
+
border-radius: 32px;
|
|
193
|
+
font-size: 1.1em;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.lm-calendar-footer select:focus {
|
|
197
|
+
background-color: #eee;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.lm-calendar-footer select:hover {
|
|
201
|
+
background-color: #eee;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.lm-calendar-footer input {
|
|
205
|
+
border: transparent;
|
|
206
|
+
padding: 8px;
|
|
207
|
+
background-color: #eee;
|
|
208
|
+
width: 100%;
|
|
209
|
+
cursor: pointer;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.lm-calendar-input {
|
|
213
|
+
padding-right: 24px !important;
|
|
214
|
+
box-sizing: border-box;
|
|
215
|
+
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M200-80q-33 0-56.5-23.5T120-160v-560q0-33 23.5-56.5T200-800h40v-80h80v80h320v-80h80v80h40q33 0 56.5 23.5T840-720v560q0 33-23.5 56.5T760-80H200Zm0-80h560v-400H200v400Zm0-480h560v-80H200v80Zm0 0v-80 80Zm280 240q-17 0-28.5-11.5T440-440q0-17 11.5-28.5T480-480q17 0 28.5 11.5T520-440q0 17-11.5 28.5T480-400Zm-160 0q-17 0-28.5-11.5T280-440q0-17 11.5-28.5T320-480q17 0 28.5 11.5T360-440q0 17-11.5 28.5T320-400Zm320 0q-17 0-28.5-11.5T600-440q0-17 11.5-28.5T640-480q17 0 28.5 11.5T680-440q0 17-11.5 28.5T640-400ZM480-240q-17 0-28.5-11.5T440-280q0-17 11.5-28.5T480-320q17 0 28.5 11.5T520-280q0 17-11.5 28.5T480-240Zm-160 0q-17 0-28.5-11.5T280-280q0-17 11.5-28.5T320-320q17 0 28.5 11.5T360-280q0 17-11.5 28.5T320-240Zm320 0q-17 0-28.5-11.5T600-280q0-17 11.5-28.5T640-320q17 0 28.5 11.5T680-280q0 17-11.5 28.5T640-240Z"/></svg>') top 50% right 0 no-repeat, content-box;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.ripple {
|
|
219
|
+
background-position: center;
|
|
220
|
+
transition: background 0.8s;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.ripple:hover {
|
|
224
|
+
background: #E0E0E0 radial-gradient(circle, transparent 1%, #E0E0E0 1%) center/15000%;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.ripple:active {
|
|
228
|
+
background-color: #E0E0E0;
|
|
229
|
+
background-size: 100%;
|
|
230
|
+
transition: background 0s;
|
|
231
|
+
}
|
package/dist/vue.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { h, getCurrentInstance } from 'vue';
|
|
2
|
+
import component from "./index";
|
|
3
|
+
|
|
4
|
+
import "./style.css";
|
|
5
|
+
import "@lemonadejs/modal/dist/style.css"
|
|
6
|
+
|
|
7
|
+
export const Calendar = {
|
|
8
|
+
inheritAttrs: false,
|
|
9
|
+
mounted() {
|
|
10
|
+
const { attrs } = getCurrentInstance();
|
|
11
|
+
|
|
12
|
+
let options = {
|
|
13
|
+
...attrs
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
this.el = this.$refs.container;
|
|
17
|
+
|
|
18
|
+
this.current = component(this.$refs.container, options);
|
|
19
|
+
},
|
|
20
|
+
setup() {
|
|
21
|
+
let containerProps = {
|
|
22
|
+
ref: 'container',
|
|
23
|
+
style: {
|
|
24
|
+
width: '100%',
|
|
25
|
+
height: '100%',
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
return () => h('div', containerProps);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default Calendar;
|
package/package.json
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@lemonadejs/calendar",
|
|
3
|
-
"title": "LemonadeJS calendar",
|
|
4
|
-
"description": "LemonadeJS calendar
|
|
5
|
-
"author": {
|
|
6
|
-
"name": "Contact <contact@lemonadejs.net>",
|
|
7
|
-
"url": "https://lemonadejs.net"
|
|
8
|
-
},
|
|
9
|
-
"keywords": [
|
|
10
|
-
"javascript calendar",
|
|
11
|
-
"lemonadejs plugins",
|
|
12
|
-
"js",
|
|
13
|
-
"library",
|
|
14
|
-
"javascript plugins"
|
|
15
|
-
],
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"lemonadejs": "^
|
|
18
|
-
"
|
|
19
|
-
},
|
|
20
|
-
"main": "dist/index.js",
|
|
21
|
-
"
|
|
22
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@lemonadejs/calendar",
|
|
3
|
+
"title": "LemonadeJS calendar",
|
|
4
|
+
"description": "LemonadeJS reactive JavaScript calendar plugin",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Contact <contact@lemonadejs.net>",
|
|
7
|
+
"url": "https://lemonadejs.net"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"javascript calendar",
|
|
11
|
+
"lemonadejs plugins",
|
|
12
|
+
"js",
|
|
13
|
+
"library",
|
|
14
|
+
"javascript plugins"
|
|
15
|
+
],
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"lemonadejs": "^4.0.1",
|
|
18
|
+
"@lemonadejs/modal": "^2.3.3"
|
|
19
|
+
},
|
|
20
|
+
"main": "dist/index.js",
|
|
21
|
+
"types": "dist/index.d.ts",
|
|
22
|
+
"version": "3.0.3"
|
|
23
|
+
}
|