@event-calendar/core 1.5.0 → 2.0.0
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 +91 -59
- package/index.js +277 -255
- package/package.json +2 -2
- package/src/Buttons.svelte +9 -9
- package/src/lib/a11y.js +30 -0
- package/src/lib/actions.js +4 -10
- package/src/lib/date.js +9 -77
- package/src/lib/dom.js +7 -5
- package/src/lib/events.js +12 -8
- package/src/lib/stores.js +6 -7
- package/src/lib/utils.js +4 -4
- package/src/lib.js +1 -0
- package/src/storage/state.js +20 -17
- package/src/storage/stores.js +5 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@event-calendar/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"title": "Event Calendar Core package",
|
|
5
5
|
"description": "Full-sized drag & drop event calendar with resource view",
|
|
6
6
|
"keywords": [
|
|
@@ -27,6 +27,6 @@
|
|
|
27
27
|
"./package.json": "./package.json"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"svelte": "^
|
|
30
|
+
"svelte": "^4.1.1"
|
|
31
31
|
}
|
|
32
32
|
}
|
package/src/Buttons.svelte
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import {getContext} from 'svelte';
|
|
3
|
-
import {createDate, cloneDate, subtractDay, addDuration, subtractDuration, setMidnight} from './lib.js';
|
|
3
|
+
import {createDate, cloneDate, subtractDay, addDuration, setContent, subtractDuration, setMidnight} from './lib.js';
|
|
4
4
|
|
|
5
5
|
export let buttons;
|
|
6
6
|
|
|
@@ -26,16 +26,16 @@
|
|
|
26
26
|
</script>
|
|
27
27
|
|
|
28
28
|
{#each buttons as button}
|
|
29
|
-
{#if button == ''}
|
|
30
|
-
|
|
31
|
-
<h2 class="{$theme.title}"
|
|
29
|
+
{#if button == 'title'}
|
|
30
|
+
<!-- svelte-ignore a11y-missing-content -->
|
|
31
|
+
<h2 class="{$theme.title}" use:setContent={$_viewTitle}></h2>
|
|
32
32
|
{:else if button == 'prev'}
|
|
33
|
-
<button class="{$theme.button} ec-{button}" on:click={prev}><i class="{$theme.icon} ec-{button}"></i></button>
|
|
34
|
-
{:else if button
|
|
35
|
-
<button class="{$theme.button} ec-{button}" on:click={next}><i class="{$theme.icon} ec-{button}"></i></button>
|
|
36
|
-
{:else if button
|
|
33
|
+
<button class="{$theme.button} ec-{button}" aria-label={$buttonText.prev} on:click={prev}><i class="{$theme.icon} ec-{button}"></i></button>
|
|
34
|
+
{:else if button == 'next'}
|
|
35
|
+
<button class="{$theme.button} ec-{button}" aria-label={$buttonText.next} on:click={next}><i class="{$theme.icon} ec-{button}"></i></button>
|
|
36
|
+
{:else if button == 'today'}
|
|
37
37
|
<button class="{$theme.button} ec-{button}" on:click={() => $date = cloneDate(today)} disabled={isToday}>{$buttonText[button]}</button>
|
|
38
|
-
{:else}
|
|
38
|
+
{:else if button != ''}
|
|
39
39
|
<button class="{$theme.button}{$view === button ? ' ' + $theme.active : ''} ec-{button}" on:click={() => $view = button}>{$buttonText[button]}</button>
|
|
40
40
|
{/if}
|
|
41
41
|
{/each}
|
package/src/lib/a11y.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
export function keyEnter(fn) {
|
|
3
|
+
return function (e) {
|
|
4
|
+
return e.key === 'Enter' || e.key === ' ' ? fn.call(this, e) : undefined;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function btnTextDay(text) {
|
|
9
|
+
return btnText(text, 'day');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function btnTextWeek(text) {
|
|
13
|
+
return btnText(text, 'week');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function btnTextMonth(text) {
|
|
17
|
+
return btnText(text, 'month');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function btnTextYear(text) {
|
|
21
|
+
return btnText(text, 'year');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function btnText(text, period) {
|
|
25
|
+
return {
|
|
26
|
+
...text,
|
|
27
|
+
next: 'Next ' + period,
|
|
28
|
+
prev: 'Previous ' + period
|
|
29
|
+
};
|
|
30
|
+
}
|
package/src/lib/actions.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
|
-
import {isObject} from './utils.js';
|
|
2
1
|
|
|
3
2
|
export function setContent(node, content) {
|
|
4
3
|
let actions = {
|
|
5
4
|
update(content) {
|
|
6
|
-
|
|
7
|
-
node.removeChild(node.lastChild);
|
|
8
|
-
}
|
|
9
|
-
if (!isObject(content)) {
|
|
5
|
+
if (typeof content == 'string') {
|
|
10
6
|
node.innerText = content;
|
|
11
|
-
} else if (content
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
} else if (content.html) {
|
|
7
|
+
} else if (content?.domNodes) {
|
|
8
|
+
node.replaceChildren(...content.domNodes);
|
|
9
|
+
} else if (content?.html) {
|
|
16
10
|
node.innerHTML = content.html;
|
|
17
11
|
}
|
|
18
12
|
}
|
package/src/lib/date.js
CHANGED
|
@@ -91,43 +91,6 @@ export function toISOString(date) {
|
|
|
91
91
|
return date.toISOString().substring(0, 19);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
export function formatRange(start, end, intl) {
|
|
95
|
-
if (start.getFullYear() !== end.getFullYear()) {
|
|
96
|
-
return intl.format(start) + ' - ' + intl.format(end);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
let diff = [];
|
|
100
|
-
if (start.getMonth() !== end.getMonth()) {
|
|
101
|
-
diff.push('month');
|
|
102
|
-
}
|
|
103
|
-
if (start.getDate() !== end.getDate()) {
|
|
104
|
-
diff.push('day');
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (!diff.length) {
|
|
108
|
-
return intl.format(start);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
let opts1 = intl.resolvedOptions();
|
|
112
|
-
let opts2 = {};
|
|
113
|
-
for (let key of diff) {
|
|
114
|
-
opts2[key] = opts1[key];
|
|
115
|
-
}
|
|
116
|
-
let intl2 = new Intl.DateTimeFormat(opts1.locale, opts2);
|
|
117
|
-
|
|
118
|
-
let full1 = intl.format(start);
|
|
119
|
-
let full2 = intl.format(end);
|
|
120
|
-
let part1 = intl2.format(start);
|
|
121
|
-
let part2 = intl2.format(end);
|
|
122
|
-
|
|
123
|
-
let common = _commonChunks(full1, part1, full2, part2);
|
|
124
|
-
if (common) {
|
|
125
|
-
return common.head + part1 + ' - ' + part2 + common.tail;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return full1 + ' - ' + full2;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
94
|
export function datesEqual(date1, ...dates2) {
|
|
132
95
|
return dates2.every(date2 => date1.getTime() === date2.getTime());
|
|
133
96
|
}
|
|
@@ -151,6 +114,15 @@ export function noTimePart(date) {
|
|
|
151
114
|
return typeof date === 'string' && date.length <= 10;
|
|
152
115
|
}
|
|
153
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Copy time from one date to another
|
|
119
|
+
*/
|
|
120
|
+
export function copyTime(toDate, fromDate) {
|
|
121
|
+
toDate.setUTCHours(fromDate.getUTCHours(), fromDate.getUTCMinutes(), fromDate.getUTCSeconds(), 0);
|
|
122
|
+
|
|
123
|
+
return toDate;
|
|
124
|
+
}
|
|
125
|
+
|
|
154
126
|
/**
|
|
155
127
|
* Private functions
|
|
156
128
|
*/
|
|
@@ -177,43 +149,3 @@ function _fromISOString(str) {
|
|
|
177
149
|
Number(parts[5] || 0)
|
|
178
150
|
));
|
|
179
151
|
}
|
|
180
|
-
|
|
181
|
-
function _commonChunks(str1, substr1, str2, substr2) {
|
|
182
|
-
let i = 0;
|
|
183
|
-
while (i < str1.length) {
|
|
184
|
-
let res1;
|
|
185
|
-
[i, res1] = _cut(str1, substr1, i);
|
|
186
|
-
if (!res1) {
|
|
187
|
-
break;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
let j = 0;
|
|
191
|
-
while (j < str2.length) {
|
|
192
|
-
let res2;
|
|
193
|
-
[j, res2] = _cut(str2, substr2, j);
|
|
194
|
-
if (!res2) {
|
|
195
|
-
break;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (res1.head === res2.head && res1.tail === res2.tail) {
|
|
199
|
-
return res1;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return null
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function _cut(str, substr, from) {
|
|
208
|
-
let start = str.indexOf(substr, from);
|
|
209
|
-
if (start >= 0) {
|
|
210
|
-
let end = start + substr.length;
|
|
211
|
-
|
|
212
|
-
return [end, {
|
|
213
|
-
head: str.substr(0, start),
|
|
214
|
-
tail: str.substr(end)
|
|
215
|
-
}];
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return [-1, null];
|
|
219
|
-
}
|
package/src/lib/dom.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import {symbol} from './utils.js';
|
|
2
2
|
|
|
3
|
-
export function createElement(tag, className,
|
|
3
|
+
export function createElement(tag, className, content) {
|
|
4
4
|
let el = document.createElement(tag);
|
|
5
5
|
el.className = className;
|
|
6
|
-
if (
|
|
7
|
-
el.
|
|
8
|
-
} else if (
|
|
9
|
-
el.
|
|
6
|
+
if (typeof content == 'string') {
|
|
7
|
+
el.innerText = content;
|
|
8
|
+
} else if (content.domNodes) {
|
|
9
|
+
el.replaceChildren(...content.domNodes);
|
|
10
|
+
} else if (content.html) {
|
|
11
|
+
el.innerHTML = content.html;
|
|
10
12
|
}
|
|
11
13
|
return el;
|
|
12
14
|
}
|
package/src/lib/events.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {addDay, datesEqual, createDate, cloneDate, setMidnight, toLocalDate, noTimePart} from './date';
|
|
1
|
+
import {addDay, datesEqual, createDate, cloneDate, setMidnight, toLocalDate, noTimePart, copyTime} from './date';
|
|
2
2
|
import {createElement} from './dom';
|
|
3
3
|
import {assign} from './utils';
|
|
4
4
|
import {toViewWithLocalDates} from './view';
|
|
@@ -131,10 +131,14 @@ export function repositionEvent(chunk, longChunks, height) {
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
export function createEventContent(chunk, displayEventEnd, eventContent, theme, _intlEventTime, _view) {
|
|
134
|
-
let timeText = _intlEventTime.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
134
|
+
let timeText = _intlEventTime.formatRange(
|
|
135
|
+
chunk.start,
|
|
136
|
+
displayEventEnd && chunk.event.display !== 'pointer'
|
|
137
|
+
? copyTime(cloneDate(chunk.start), chunk.end) // make Intl.formatRange output only the time part
|
|
138
|
+
: chunk.start
|
|
139
|
+
);
|
|
140
|
+
let content;
|
|
141
|
+
|
|
138
142
|
if (eventContent) {
|
|
139
143
|
content = is_function(eventContent)
|
|
140
144
|
? eventContent({
|
|
@@ -150,14 +154,14 @@ export function createEventContent(chunk, displayEventEnd, eventContent, theme,
|
|
|
150
154
|
break;
|
|
151
155
|
case 'pointer':
|
|
152
156
|
content = {
|
|
153
|
-
domNodes: [createElement('div', theme.eventTime,
|
|
157
|
+
domNodes: [createElement('div', theme.eventTime, timeText)]
|
|
154
158
|
};
|
|
155
159
|
break;
|
|
156
160
|
default:
|
|
157
161
|
content = {
|
|
158
162
|
domNodes: [
|
|
159
|
-
...chunk.event.allDay ? [] : [createElement('div', theme.eventTime,
|
|
160
|
-
createElement('div', theme.eventTitle, chunk.event.
|
|
163
|
+
...chunk.event.allDay ? [] : [createElement('div', theme.eventTime, timeText)],
|
|
164
|
+
createElement('div', theme.eventTitle, chunk.event.title)
|
|
161
165
|
]
|
|
162
166
|
};
|
|
163
167
|
}
|
package/src/lib/stores.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {derived, writable, get} from 'svelte/store';
|
|
2
2
|
import {is_function} from 'svelte/internal';
|
|
3
|
-
import {toLocalDate
|
|
3
|
+
import {toLocalDate} from './date';
|
|
4
4
|
|
|
5
5
|
export function writable2(value, parser, start) {
|
|
6
6
|
return {
|
|
7
|
-
...writable(
|
|
7
|
+
...writable(value, start),
|
|
8
8
|
parse: parser
|
|
9
9
|
};
|
|
10
10
|
}
|
|
@@ -43,12 +43,11 @@ export function intl(locale, format) {
|
|
|
43
43
|
|
|
44
44
|
export function intlRange(locale, format) {
|
|
45
45
|
return derived([locale, format], ([$locale, $format]) => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
let intl = new Intl.DateTimeFormat($locale, $format);
|
|
46
|
+
let intl = is_function($format)
|
|
47
|
+
? {formatRange: $format}
|
|
48
|
+
: new Intl.DateTimeFormat($locale, $format);
|
|
50
49
|
return {
|
|
51
|
-
|
|
50
|
+
formatRange: (start, end) => intl.formatRange(toLocalDate(start), toLocalDate(end))
|
|
52
51
|
};
|
|
53
52
|
});
|
|
54
53
|
}
|
package/src/lib/utils.js
CHANGED
|
@@ -2,6 +2,10 @@ export function assign(...args) {
|
|
|
2
2
|
return Object.assign(...args);
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
export function keys(object) {
|
|
6
|
+
return Object.keys(object);
|
|
7
|
+
}
|
|
8
|
+
|
|
5
9
|
export function floor(value) {
|
|
6
10
|
return Math.floor(value);
|
|
7
11
|
}
|
|
@@ -14,10 +18,6 @@ export function max(...args) {
|
|
|
14
18
|
return Math.max(...args);
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
export function isObject(test) {
|
|
18
|
-
return typeof test === 'object' && test !== null;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
21
|
export function symbol() {
|
|
22
22
|
return Symbol('ec');
|
|
23
23
|
}
|
package/src/lib.js
CHANGED
package/src/storage/state.js
CHANGED
|
@@ -12,16 +12,20 @@ import {
|
|
|
12
12
|
viewTitle,
|
|
13
13
|
view as view2 // hack to avoid a runtime error in SvelteKit dev mode (ReferenceError: view is not defined)
|
|
14
14
|
} from './stores';
|
|
15
|
-
import {assign, writable2, intl, intlRange} from '../lib.js';
|
|
15
|
+
import {assign, keys, writable2, intl, intlRange} from '../lib.js';
|
|
16
16
|
|
|
17
17
|
export default class {
|
|
18
18
|
constructor(plugins, input) {
|
|
19
19
|
plugins = plugins || [];
|
|
20
20
|
|
|
21
21
|
// Create options
|
|
22
|
-
let options
|
|
22
|
+
let options= createOptions(plugins);
|
|
23
23
|
let parsers = createParsers(options, plugins);
|
|
24
24
|
|
|
25
|
+
// Parse options
|
|
26
|
+
options = parseOpts(options, parsers);
|
|
27
|
+
input = parseOpts(input, parsers);
|
|
28
|
+
|
|
25
29
|
// Create stores for options
|
|
26
30
|
for (let [option, value] of Object.entries(options)) {
|
|
27
31
|
this[option] = writable2(value, parsers[option]);
|
|
@@ -37,10 +41,10 @@ export default class {
|
|
|
37
41
|
this._events = events(this);
|
|
38
42
|
this._now = now();
|
|
39
43
|
this._today = today(this);
|
|
40
|
-
this._intlEventTime =
|
|
44
|
+
this._intlEventTime = intlRange(this.locale, this.eventTimeFormat);
|
|
41
45
|
this._intlSlotLabel = intl(this.locale, this.slotLabelFormat);
|
|
42
46
|
this._intlDayHeader = intl(this.locale, this.dayHeaderFormat);
|
|
43
|
-
this.
|
|
47
|
+
this._intlTitle = intlRange(this.locale, this.titleFormat);
|
|
44
48
|
this._bodyEl = writable(undefined);
|
|
45
49
|
this._scrollable = writable(false);
|
|
46
50
|
this._viewTitle = viewTitle(this);
|
|
@@ -68,13 +72,9 @@ export default class {
|
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
// Set options for each view
|
|
71
|
-
let
|
|
72
|
-
parseOpts(commonOpts, this);
|
|
73
|
-
let views = new Set([...Object.keys(options.views), ...Object.keys(input.views || {})]);
|
|
75
|
+
let views = new Set([...keys(options.views), ...keys(input.views ?? {})]);
|
|
74
76
|
for (let view of views) {
|
|
75
|
-
let
|
|
76
|
-
parseOpts(viewOpts, this);
|
|
77
|
-
let opts = assign({}, commonOpts, viewOpts);
|
|
77
|
+
let opts = assign({}, options, options.views[view] ?? {}, input, input.views?.[view] ?? {});
|
|
78
78
|
// Change view component when view changes
|
|
79
79
|
this.view.subscribe(newView => {
|
|
80
80
|
if (newView === view) {
|
|
@@ -85,7 +85,7 @@ export default class {
|
|
|
85
85
|
}
|
|
86
86
|
});
|
|
87
87
|
// Process options
|
|
88
|
-
for (let key of
|
|
88
|
+
for (let key of keys(opts)) {
|
|
89
89
|
if (this.hasOwnProperty(key) && key[0] !== '_') {
|
|
90
90
|
let {set, _set, ...rest} = this[key];
|
|
91
91
|
|
|
@@ -113,12 +113,15 @@ export default class {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
function parseOpts(opts,
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
116
|
+
function parseOpts(opts, parsers) {
|
|
117
|
+
let result = {};
|
|
118
|
+
for (let key of keys(opts)) {
|
|
119
|
+
result[key] = parsers[key] ? parsers[key](opts[key]) : opts[key];
|
|
120
|
+
}
|
|
121
|
+
if (opts.views) {
|
|
122
|
+
for (let view of keys(opts.views)) {
|
|
123
|
+
result.views[view] = parseOpts(opts.views[view], parsers);
|
|
122
124
|
}
|
|
123
125
|
}
|
|
126
|
+
return result;
|
|
124
127
|
}
|
package/src/storage/stores.js
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from '../lib.js';
|
|
21
21
|
|
|
22
22
|
export function monthMode(state) {
|
|
23
|
-
return derived(state.view, $view => $view
|
|
23
|
+
return derived(state.view, $view => $view?.startsWith('dayGrid'));
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export function activeRange(state) {
|
|
@@ -93,11 +93,11 @@ export function viewDates(state) {
|
|
|
93
93
|
|
|
94
94
|
export function viewTitle(state) {
|
|
95
95
|
return derived(
|
|
96
|
-
[state.date, state._activeRange, state.
|
|
97
|
-
([$date, $_activeRange, $
|
|
96
|
+
[state.date, state._activeRange, state._intlTitle, state._monthMode],
|
|
97
|
+
([$date, $_activeRange, $_intlTitle, $_monthMode]) => {
|
|
98
98
|
return $_monthMode
|
|
99
|
-
? $
|
|
100
|
-
: $
|
|
99
|
+
? $_intlTitle.formatRange($date, $date)
|
|
100
|
+
: $_intlTitle.formatRange($_activeRange.start, subtractDay(cloneDate($_activeRange.end)));
|
|
101
101
|
}
|
|
102
102
|
);
|
|
103
103
|
}
|