@event-calendar/core 5.3.1 → 5.3.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 CHANGED
@@ -5,7 +5,7 @@ See [demo](https://vkurko.github.io/calendar/) and [changelog](CHANGELOG.md).
5
5
  Full-sized drag & drop JavaScript event calendar with resource & timeline views:
6
6
 
7
7
  * Lightweight (35kb [br](https://en.wikipedia.org/wiki/Brotli) compressed)
8
- * 100% human-coded
8
+ * Feature-rich, performant, and with minimal DOM structure (thanks to [CSS Grid](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Grid_layout))
9
9
  * Zero-dependency (standalone bundle)
10
10
  * Used on over 70,000 websites with [Bookly](https://wordpress.org/plugins/bookly-responsive-appointment-booking-tool/)
11
11
 
@@ -251,8 +251,8 @@ This bundle contains a version of the calendar that includes all plugins and is
251
251
 
252
252
  The first step is to include the following lines of code in the `<head>` section of your page:
253
253
  ```html
254
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@event-calendar/build@5.3.1/dist/event-calendar.min.css">
255
- <script src="https://cdn.jsdelivr.net/npm/@event-calendar/build@5.3.1/dist/event-calendar.min.js"></script>
254
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@event-calendar/build@5.3.3/dist/event-calendar.min.css">
255
+ <script src="https://cdn.jsdelivr.net/npm/@event-calendar/build@5.3.3/dist/event-calendar.min.js"></script>
256
256
  ```
257
257
 
258
258
  <details>
package/dist/index.css CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * EventCalendar v5.3.1
2
+ * EventCalendar v5.3.3
3
3
  * https://github.com/vkurko/calendar
4
4
  */
5
5
  .ec {
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * EventCalendar v5.3.1
2
+ * EventCalendar v5.3.3
3
3
  * https://github.com/vkurko/calendar
4
4
  */
5
5
  import { untrack, tick, getAbortSignal, getContext, setContext, onMount, mount, unmount } from "svelte";
@@ -72,6 +72,9 @@ function keys(object) {
72
72
  function entries(object) {
73
73
  return Object.entries(object);
74
74
  }
75
+ function hasOwn(object, property) {
76
+ return Object.hasOwn(object, property);
77
+ }
75
78
  function floor(value) {
76
79
  return Math.floor(value);
77
80
  }
@@ -770,16 +773,52 @@ function createSlotTimeLimits(slotMinTime, slotMaxTime, flexibleSlotTimeLimits,
770
773
  }
771
774
  return { min: min$1, max: max$1 };
772
775
  }
776
+ function arrayProxy(array) {
777
+ let counter = 0;
778
+ let version = $.state($.proxy(counter));
779
+ return proxy(array, () => $.get(version), () => true, () => $.set(version, ++counter, true));
780
+ }
781
+ function objectProxy(object) {
782
+ let counter = 0;
783
+ let versions = $.proxy({});
784
+ return proxy(object, (prop) => versions[prop], (a, b) => a !== b, (prop) => versions[prop] = ++counter);
785
+ }
786
+ function proxy(target, setDependency, hasEffect, invokeEffect) {
787
+ return new Proxy(target, {
788
+ get(target2, prop, receiver) {
789
+ if (hasOwn(target2, prop)) {
790
+ setDependency(prop);
791
+ }
792
+ return Reflect.get(target2, prop, receiver);
793
+ },
794
+ set(target2, prop, value, receiver) {
795
+ let has = hasEffect(target2[prop], value);
796
+ let result = Reflect.set(target2, prop, value, receiver);
797
+ if (has) {
798
+ invokeEffect(prop);
799
+ }
800
+ return result;
801
+ }
802
+ });
803
+ }
773
804
  function createOptions(plugins) {
774
805
  let options = {
775
- buttonText: { today: "today" },
806
+ buttonText: {
807
+ today: "today"
808
+ },
776
809
  customButtons: {},
777
810
  customScrollbars: false,
778
811
  // ec option
779
812
  date: /* @__PURE__ */ new Date(),
780
813
  datesSet: void 0,
781
- dayHeaderFormat: { weekday: "short", month: "numeric", day: "numeric" },
782
- dayHeaderAriaLabelFormat: { dateStyle: "full" },
814
+ dayHeaderFormat: {
815
+ weekday: "short",
816
+ month: "numeric",
817
+ day: "numeric"
818
+ },
819
+ dayHeaderAriaLabelFormat: {
820
+ dateStyle: "full"
821
+ },
783
822
  displayEventEnd: true,
784
823
  duration: { weeks: 1 },
785
824
  events: [],
@@ -797,10 +836,17 @@ function createOptions(plugins) {
797
836
  eventOrder: void 0,
798
837
  eventSources: [],
799
838
  eventTextColor: void 0,
800
- eventTimeFormat: { hour: "numeric", minute: "2-digit" },
839
+ eventTimeFormat: {
840
+ hour: "numeric",
841
+ minute: "2-digit"
842
+ },
801
843
  filterEventsWithResources: false,
802
844
  firstDay: 0,
803
- headerToolbar: { start: "title", center: "", end: "today prev,next" },
845
+ headerToolbar: {
846
+ start: "title",
847
+ center: "",
848
+ end: "today prev,next"
849
+ },
804
850
  height: void 0,
805
851
  hiddenDays: [],
806
852
  highlightedDates: [],
@@ -843,17 +889,13 @@ function createOptions(plugins) {
843
889
  title: "ec-title",
844
890
  toolbar: "ec-toolbar",
845
891
  view: "",
846
- weekdays: [
847
- "ec-sun",
848
- "ec-mon",
849
- "ec-tue",
850
- "ec-wed",
851
- "ec-thu",
852
- "ec-fri",
853
- "ec-sat"
854
- ]
892
+ weekdays: ["ec-sun", "ec-mon", "ec-tue", "ec-wed", "ec-thu", "ec-fri", "ec-sat"]
893
+ },
894
+ titleFormat: {
895
+ year: "numeric",
896
+ month: "short",
897
+ day: "numeric"
855
898
  },
856
- titleFormat: { year: "numeric", month: "short", day: "numeric" },
857
899
  validRange: void 0,
858
900
  view: void 0,
859
901
  viewDidMount: void 0,
@@ -881,72 +923,66 @@ function createParsers(plugins) {
881
923
  return parsers;
882
924
  }
883
925
  const specialOptions = ["buttonText", "customButtons", "theme"];
884
- function optionsState(mainState, plugins, userOptions) {
926
+ function optionsState(plugins, userOptions) {
885
927
  let defOptions = createOptions(plugins);
886
928
  let parsers = createParsers(plugins);
887
929
  defOptions = parseOptions(defOptions, parsers);
888
930
  userOptions = parseOptions(userOptions, parsers);
889
- let defViewsOptions = extractOption(defOptions, "views") ?? {};
890
- let userViewsOptions = extractOption(userOptions, "views") ?? {};
891
- let options = new SvelteMap();
892
- setOptions(options, defOptions);
931
+ let defViews = extractOption(defOptions, "views") ?? {};
932
+ let userViews = extractOption(userOptions, "views") ?? {};
933
+ let options = objectProxy({});
934
+ assign(options, defOptions);
935
+ if (userOptions.view) {
936
+ options.view = userOptions.view;
937
+ }
893
938
  let setters = {};
894
- let currentOpts = {};
895
- function initEffects() {
896
- if (userOptions.view) {
897
- options.set("view", userOptions.view);
898
- }
899
- let views = /* @__PURE__ */ new Set([...keys(defViewsOptions), ...keys(userViewsOptions)]);
900
- for (let view2 of views) {
901
- let userViewOptions = userViewsOptions[view2] ?? {};
902
- let defOpts = mergeOpts(defOptions, defViewsOptions[view2] ?? defViewsOptions[userViewOptions.type] ?? {});
903
- let opts = mergeOpts(defOpts, userOptions, userViewOptions);
904
- let component = extractOption(opts, "component");
905
- delete opts.view;
906
- for (let key of keys(opts)) {
907
- if (options.has(key)) {
908
- if (!setters[key]) {
909
- setters[key] = [];
910
- }
911
- setters[key].push(specialOptions.includes(key) ? (value) => opts[key] = isFunction(value) ? value(defOpts[key]) : value : (value) => opts[key] = value);
912
- } else {
913
- delete opts[key];
939
+ let viewOptions = {};
940
+ let viewComponents = {};
941
+ let views = /* @__PURE__ */ new Set([...keys(defViews), ...keys(userViews)]);
942
+ for (let view2 of views) {
943
+ let userViewOptions = userViews[view2] ?? {};
944
+ let defOpts = mergeOpts(defOptions, defViews[view2] ?? defViews[userViewOptions.type] ?? {});
945
+ let opts = mergeOpts(defOpts, userOptions, userViewOptions);
946
+ let component = extractOption(opts, "component");
947
+ delete opts.view;
948
+ for (let key of keys(opts)) {
949
+ if (hasOwn(options, key)) {
950
+ if (!setters[key]) {
951
+ setters[key] = [];
914
952
  }
953
+ setters[key].push(
954
+ specialOptions.includes(key) ? (value) => opts[key] = isFunction(value) ? value(defOpts[key]) : value : (value) => opts[key] = value
955
+ );
956
+ } else {
957
+ delete opts[key];
915
958
  }
916
- $.user_pre_effect(() => {
917
- let newView = options.get("view");
918
- untrack(() => {
919
- if (newView === view2) {
920
- mainState.setViewComponent(component);
921
- currentOpts = opts;
922
- setOptions(options, opts);
923
- }
924
- });
925
- });
926
959
  }
927
- }
928
- return {
929
- proxy: new Proxy(options, {
930
- set(options2, key, value) {
931
- currentOpts[key] = value;
932
- options2.set(key, value);
933
- return true;
934
- },
935
- get(options2, key) {
936
- return options2.get(key);
937
- }
938
- }),
939
- setOption(key, value, parsed) {
940
- if (options.has(key)) {
941
- if (!parsed && key in parsers) {
942
- value = parsers[key](value);
960
+ viewOptions[view2] = opts;
961
+ viewComponents[view2] = component;
962
+ }
963
+ assign(options, viewOptions[options.view]);
964
+ return [
965
+ options,
966
+ function setOption(key, value, parsed = true) {
967
+ if (hasOwn(options, key)) {
968
+ if (!parsed) {
969
+ if (key in parsers) {
970
+ value = parsers[key](value);
971
+ } else if (isPlainObject(value)) {
972
+ value = { ...value };
973
+ } else if (isArray(value)) {
974
+ value = [...value];
975
+ }
943
976
  }
944
977
  setters[key]?.forEach((set) => set(value));
945
- options.set(key, currentOpts[key] ?? value);
978
+ options[key] = value;
946
979
  }
947
980
  },
948
- initEffects
949
- };
981
+ function setViewOptions(view2) {
982
+ assign(options, viewOptions[view2]);
983
+ return viewComponents[view2];
984
+ }
985
+ ];
950
986
  }
951
987
  function parseOptions(opts, parsers) {
952
988
  let result = { ...opts };
@@ -977,15 +1013,14 @@ function mergeOpts(...args) {
977
1013
  override[key] = opts[key](result[key]);
978
1014
  }
979
1015
  }
980
- result = { ...result, ...opts, ...override };
1016
+ result = {
1017
+ ...result,
1018
+ ...opts,
1019
+ ...override
1020
+ };
981
1021
  }
982
1022
  return result;
983
1023
  }
984
- function setOptions(map, options) {
985
- for (let [key, value] of entries(options)) {
986
- map.set(key, value);
987
- }
988
- }
989
1024
  function diff(options, prevOptions) {
990
1025
  let diff2 = [];
991
1026
  for (let key of keys(options)) {
@@ -995,6 +1030,17 @@ function diff(options, prevOptions) {
995
1030
  }
996
1031
  return diff2;
997
1032
  }
1033
+ function switchView(mainState) {
1034
+ return () => {
1035
+ let { options: { view: view2 } } = mainState;
1036
+ untrack(() => {
1037
+ let initComponent = mainState.setViewOptions(view2);
1038
+ mainState.extensions = {};
1039
+ mainState.features = [];
1040
+ mainState.viewComponent = initComponent(mainState);
1041
+ });
1042
+ };
1043
+ }
998
1044
  function loadEvents(mainState, loadingInvoker) {
999
1045
  return () => {
1000
1046
  let {
@@ -1008,7 +1054,7 @@ function loadEvents(mainState, loadingInvoker) {
1008
1054
  eventSources.map((source) => isFunction(source.events) ? source.events : source),
1009
1055
  events,
1010
1056
  createEvents,
1011
- (result) => mainState.events = result,
1057
+ (result) => mainState.events = arrayProxy(result),
1012
1058
  activeRange2,
1013
1059
  fetchedRange,
1014
1060
  viewDates2,
@@ -1032,7 +1078,7 @@ function loadResources(mainState, loadingInvoker) {
1032
1078
  isArray(resources) ? [] : [resources],
1033
1079
  resources,
1034
1080
  createResources,
1035
- (result) => mainState.resources = result,
1081
+ (result) => mainState.resources = arrayProxy(result),
1036
1082
  activeRange2,
1037
1083
  fetchedRange,
1038
1084
  viewDates2,
@@ -1099,10 +1145,10 @@ function load(sources, defaultResult, parseResult, applyResult, activeRange2, fe
1099
1145
  assign(fetchedRange, activeRange2);
1100
1146
  }
1101
1147
  }
1102
- function createLoadingInvoker(options) {
1148
+ function createLoadingInvoker(mainState) {
1103
1149
  let counter = 0;
1104
1150
  function invoke(value) {
1105
- let { loading } = options;
1151
+ let { options: { loading } } = mainState;
1106
1152
  if (isFunction(loading)) {
1107
1153
  loading(value);
1108
1154
  }
@@ -1434,20 +1480,20 @@ class State {
1434
1480
  set iClass(value) {
1435
1481
  $.set(this.#iClass, value, true);
1436
1482
  }
1437
- #setOption;
1483
+ options;
1484
+ setOption;
1485
+ setViewOptions;
1438
1486
  constructor(plugins, options) {
1439
- let { proxy, setOption, initEffects } = optionsState(this, plugins, options);
1440
- this.options = proxy;
1441
- this.#setOption = setOption;
1487
+ [this.options, this.setOption, this.setViewOptions] = optionsState(plugins, options);
1442
1488
  this.#auxComponents = $.state($.proxy([]));
1443
1489
  this.#currentRange = $.derived(currentRange(this));
1444
1490
  this.#activeRange = $.derived(activeRange(this));
1445
1491
  this.#fetchedRange = $.state($.proxy({ events: {}, resources: {} }));
1446
- this.#events = $.state([]);
1492
+ this.#events = $.state(arrayProxy(this.options.events));
1447
1493
  this.#filteredEvents = $.derived(filteredEvents(this));
1448
1494
  this.#mainEl = $.state();
1449
1495
  this.#now = $.state($.proxy(createDate()));
1450
- this.#resources = $.state([]);
1496
+ this.#resources = $.state(arrayProxy(this.options.resources));
1451
1497
  this.#today = $.state($.proxy(setMidnight(createDate())));
1452
1498
  this.#intlEventTime = $.derived(intlRange(this, "eventTimeFormat"));
1453
1499
  this.#intlDayHeader = $.derived(intl(this, "dayHeaderFormat"));
@@ -1466,11 +1512,11 @@ class State {
1466
1512
  for (let plugin of plugins) {
1467
1513
  plugin.initState?.(this);
1468
1514
  }
1469
- initEffects();
1470
1515
  this.#initEffects();
1471
1516
  }
1472
1517
  #initEffects() {
1473
- let loading = createLoadingInvoker(this.options);
1518
+ let loading = createLoadingInvoker(this);
1519
+ $.user_pre_effect(switchView(this));
1474
1520
  $.user_pre_effect(setNowAndToday(this));
1475
1521
  $.user_effect(loadEvents(this, loading));
1476
1522
  $.user_effect(loadResources(this, loading));
@@ -1478,14 +1524,6 @@ class State {
1478
1524
  $.user_effect(runEventAllUpdated(this));
1479
1525
  $.user_effect(runViewDidMount(this));
1480
1526
  }
1481
- setViewComponent(component) {
1482
- this.extensions = {};
1483
- this.features = [];
1484
- this.viewComponent = component(this);
1485
- }
1486
- setOption(name, value, parsed = true) {
1487
- this.#setOption(name, value, parsed);
1488
- }
1489
1527
  }
1490
1528
  var root_2$5 = $.from_html(`<h2></h2>`);
1491
1529
  var root_4$1 = $.from_html(`<button><i></i></button>`);
@@ -1783,12 +1821,6 @@ function Calendar($$anchor, $$props) {
1783
1821
  assign(prevOptions, options());
1784
1822
  });
1785
1823
  function setOption(name, value) {
1786
- if (isPlainObject(value)) {
1787
- value = { ...value };
1788
- }
1789
- if (isArray(value)) {
1790
- value = [...value];
1791
- }
1792
1824
  mainState.setOption(name, value, false);
1793
1825
  return this;
1794
1826
  }
@@ -1819,7 +1851,6 @@ function Calendar($$anchor, $$props) {
1819
1851
  function addEvent(event) {
1820
1852
  event = createEvents([event])[0];
1821
1853
  $.get(events).push(event);
1822
- mainState.events = [...$.get(events)];
1823
1854
  return toEventWithLocalDates(event);
1824
1855
  }
1825
1856
  function updateEvent(event) {
@@ -1828,7 +1859,6 @@ function Calendar($$anchor, $$props) {
1828
1859
  if (idx >= 0) {
1829
1860
  event = createEvents([event])[0];
1830
1861
  $.get(events)[idx] = event;
1831
- mainState.events = [...$.get(events)];
1832
1862
  return toEventWithLocalDates(event);
1833
1863
  }
1834
1864
  return null;
@@ -1838,7 +1868,6 @@ function Calendar($$anchor, $$props) {
1838
1868
  let idx = $.get(events).findIndex((event) => event.id === id);
1839
1869
  if (idx >= 0) {
1840
1870
  $.get(events).splice(idx, 1);
1841
- mainState.events = [...$.get(events)];
1842
1871
  }
1843
1872
  return this;
1844
1873
  }
@@ -1887,27 +1916,17 @@ function Calendar($$anchor, $$props) {
1887
1916
  var node = $.child(div);
1888
1917
  Toolbar(node, {});
1889
1918
  var node_1 = $.sibling(node, 2);
1890
- {
1891
- var consequent = ($$anchor2) => {
1892
- var fragment = $.comment();
1893
- var node_2 = $.first_child(fragment);
1894
- $.component(node_2, () => $.get(View2), ($$anchor3, View_12) => {
1895
- View_12($$anchor3, {});
1896
- });
1897
- $.append($$anchor2, fragment);
1898
- };
1899
- $.if(node_1, ($$render) => {
1900
- if ($.get(View2)) $$render(consequent);
1901
- });
1902
- }
1903
- var node_3 = $.sibling(node_1, 2);
1904
- $.each(node_3, 17, () => $.get(auxComponents), $.index, ($$anchor2, AuxComponent) => {
1905
- var fragment_1 = $.comment();
1906
- var node_4 = $.first_child(fragment_1);
1907
- $.component(node_4, () => $.get(AuxComponent), ($$anchor3, AuxComponent_1) => {
1919
+ $.component(node_1, () => $.get(View2), ($$anchor2, View_12) => {
1920
+ View_12($$anchor2, {});
1921
+ });
1922
+ var node_2 = $.sibling(node_1, 2);
1923
+ $.each(node_2, 17, () => $.get(auxComponents), $.index, ($$anchor2, AuxComponent) => {
1924
+ var fragment = $.comment();
1925
+ var node_3 = $.first_child(fragment);
1926
+ $.component(node_3, () => $.get(AuxComponent), ($$anchor3, AuxComponent_1) => {
1908
1927
  AuxComponent_1($$anchor3, {});
1909
1928
  });
1910
- $.append($$anchor2, fragment_1);
1929
+ $.append($$anchor2, fragment);
1911
1930
  });
1912
1931
  $.reset(div);
1913
1932
  $.template_effect(
@@ -3352,7 +3371,7 @@ function Action($$anchor, $$props) {
3352
3371
  addDuration(iEvent.end, extraDuration);
3353
3372
  }
3354
3373
  event.display = "ghost";
3355
- mainState.events = [...$.get(events)];
3374
+ $.get(events).length = $.get(events).length;
3356
3375
  }
3357
3376
  function createIEventSelect() {
3358
3377
  iEvent = {
@@ -3380,7 +3399,7 @@ function Action($$anchor, $$props) {
3380
3399
  }
3381
3400
  function updateEvent(target, source) {
3382
3401
  copyIEventData(target, source);
3383
- mainState.events = [...$.get(events)];
3402
+ $.get(events).length = $.get(events).length;
3384
3403
  }
3385
3404
  function updateIEvent(source) {
3386
3405
  iEvent = copyIEventData(iEvent, source);
@@ -5560,8 +5579,7 @@ var root_2 = $.from_html(`<button><!></button>`);
5560
5579
  var root = $.from_html(`<!> <span><!></span>`, 1);
5561
5580
  function Expander($$anchor, $$props) {
5562
5581
  $.push($$props, true);
5563
- let $$d = $.derived(() => getContext("state")), options = $.derived(() => $.get($$d).options);
5564
- let resources = $.derived(() => $.get(options).resources), theme = $.derived(() => $.get(options).theme);
5582
+ let $$d = $.derived(() => getContext("state")), resources = $.derived(() => $.get($$d).resources), theme = $.derived(() => $.get($$d).options.theme);
5565
5583
  let payload = $.state({});
5566
5584
  let expanded = $.state(true);
5567
5585
  $.user_pre_effect(() => {
@@ -5572,7 +5590,7 @@ function Expander($$anchor, $$props) {
5572
5590
  $.set(expanded, !$.get(expanded));
5573
5591
  $.get(payload).expanded = $.get(expanded);
5574
5592
  toggle($.get(payload).children, $.get(expanded));
5575
- $.get(options).resources = [...$.get(resources)];
5593
+ $.get(resources).length = $.get(resources).length;
5576
5594
  }
5577
5595
  function toggle(children, expand) {
5578
5596
  for (let child of children) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@event-calendar/core",
3
- "version": "5.3.1",
3
+ "version": "5.3.3",
4
4
  "title": "Event Calendar Core package",
5
5
  "description": "Full-sized drag & drop event calendar with resource & timeline views",
6
6
  "keywords": [
@@ -32,6 +32,6 @@
32
32
  "#components": "./src/lib/components/index.js"
33
33
  },
34
34
  "dependencies": {
35
- "svelte": "^5.49.0"
35
+ "svelte": "^5.50.1"
36
36
  }
37
37
  }
@@ -2,11 +2,11 @@
2
2
  import './styles/index.css';
3
3
  import {setContext, untrack} from 'svelte';
4
4
  import {
5
- assign, cloneDate, createEvents, getElementWithPayload, getPayload, isArray, isDate, isPlainObject, nextDate,
5
+ assign, cloneDate, createEvents, getElementWithPayload, getPayload, isDate, nextDate,
6
6
  prevDate, toEventWithLocalDates, toLocalDate, toViewWithLocalDates
7
7
  } from '#lib';
8
8
  import MainState from './storage/state.svelte.js';
9
- import {diff} from './storage/options.svelte.js';
9
+ import {diff} from './storage/options.js';
10
10
  import Toolbar from './Toolbar.svelte';
11
11
 
12
12
  let {plugins = [], options = {}} = $props();
@@ -33,12 +33,6 @@
33
33
  });
34
34
 
35
35
  export function setOption(name, value) {
36
- if (isPlainObject(value)) {
37
- value = {...value};
38
- }
39
- if (isArray(value)) {
40
- value = [...value];
41
- }
42
36
  mainState.setOption(name, value, false);
43
37
  return this;
44
38
  }
@@ -75,7 +69,6 @@
75
69
  export function addEvent(event) {
76
70
  event = createEvents([event])[0];
77
71
  events.push(event);
78
- mainState.events = [...events];
79
72
  return toEventWithLocalDates(event);
80
73
  }
81
74
 
@@ -85,7 +78,6 @@
85
78
  if (idx >= 0) {
86
79
  event = createEvents([event])[0];
87
80
  events[idx] = event;
88
- mainState.events = [...events];
89
81
  return toEventWithLocalDates(event);
90
82
  }
91
83
  return null;
@@ -96,7 +88,6 @@
96
88
  let idx = events.findIndex(event => event.id === id);
97
89
  if (idx >= 0) {
98
90
  events.splice(idx, 1);
99
- mainState.events = [...events];
100
91
  }
101
92
  return this;
102
93
  }
@@ -143,9 +134,7 @@
143
134
  role="{features.includes('list') ? 'list' : 'table'}"
144
135
  >
145
136
  <Toolbar/>
146
- {#if View} <!-- temporary fix for https://github.com/sveltejs/kit/issues/15109 -->
147
- <View/>
148
- {/if}
137
+ <View/>
149
138
  {#each auxComponents as AuxComponent}
150
139
  <AuxComponent/>
151
140
  {/each}
package/src/lib/utils.js CHANGED
@@ -10,6 +10,10 @@ export function entries(object) {
10
10
  return Object.entries(object);
11
11
  }
12
12
 
13
+ export function hasOwn(object, property) {
14
+ return Object.hasOwn(object, property);
15
+ }
16
+
13
17
  export function floor(value) {
14
18
  return Math.floor(value);
15
19
  }
@@ -487,7 +487,7 @@
487
487
  addDuration(iEvent.end, extraDuration);
488
488
  }
489
489
  event.display = 'ghost';
490
- mainState.events = [...events];
490
+ events.length = events.length;
491
491
  }
492
492
 
493
493
  function createIEventSelect() {
@@ -519,7 +519,7 @@
519
519
 
520
520
  function updateEvent(target, source) {
521
521
  copyIEventData(target, source);
522
- mainState.events = [...events];
522
+ events.length = events.length;
523
523
  }
524
524
 
525
525
  function updateIEvent(source) {
@@ -4,8 +4,7 @@
4
4
 
5
5
  let {resource} = $props();
6
6
 
7
- let {options} = $derived(getContext('state'));
8
- let {resources, theme} = $derived(options);
7
+ let {resources, options: {theme}} = $derived(getContext('state'));
9
8
 
10
9
  let payload = $state.raw({});
11
10
  let expanded = $state(true);
@@ -19,7 +18,7 @@
19
18
  expanded = !expanded;
20
19
  payload.expanded = expanded;
21
20
  toggle(payload.children, expanded);
22
- options.resources = [...resources];
21
+ resources.length = resources.length;
23
22
  }
24
23
 
25
24
  function toggle(children, expand) {
@@ -3,6 +3,21 @@ import {
3
3
  assign, cloneDate, createDate, createEvents, createResources, datesEqual, empty, isArray, isFunction, setMidnight,
4
4
  toISOString, toLocalDate, toViewWithLocalDates
5
5
  } from '#lib';
6
+ import {arrayProxy} from './proxy.svelte.js';
7
+
8
+ export function switchView(mainState) {
9
+ return () => {
10
+ // Dependencies
11
+ let {options: {view}} = mainState;
12
+
13
+ untrack(() => {
14
+ let initComponent = mainState.setViewOptions(view);
15
+ mainState.extensions = {};
16
+ mainState.features = [];
17
+ mainState.viewComponent = initComponent(mainState);
18
+ });
19
+ };
20
+ }
6
21
 
7
22
  export function loadEvents(mainState, loadingInvoker) {
8
23
  return () => {
@@ -15,7 +30,7 @@ export function loadEvents(mainState, loadingInvoker) {
15
30
  eventSources.map(source => isFunction(source.events) ? source.events : source),
16
31
  events,
17
32
  createEvents,
18
- result => mainState.events = result,
33
+ result => mainState.events = arrayProxy(result),
19
34
  activeRange,
20
35
  fetchedRange,
21
36
  viewDates,
@@ -38,7 +53,7 @@ export function loadResources(mainState, loadingInvoker) {
38
53
  isArray(resources) ? [] : [resources],
39
54
  resources,
40
55
  createResources,
41
- result => mainState.resources = result,
56
+ result => mainState.resources = arrayProxy(result),
42
57
  activeRange,
43
58
  fetchedRange,
44
59
  viewDates,
@@ -124,10 +139,10 @@ function load(sources, defaultResult, parseResult, applyResult, activeRange, fet
124
139
  }
125
140
  }
126
141
 
127
- export function createLoadingInvoker(options) {
142
+ export function createLoadingInvoker(mainState) {
128
143
  let counter = 0;
129
144
  function invoke(value) {
130
- let {loading} = options;
145
+ let {options: {loading}} = mainState;
131
146
  if (isFunction(loading)) {
132
147
  loading(value);
133
148
  }
@@ -1,9 +1,9 @@
1
1
  import {untrack} from 'svelte';
2
2
  import {
3
- createDate, createDateRange, createDuration, createEvents, createEventSources, createResources, entries, isArray,
4
- isFunction, keys, setMidnight
3
+ assign, createDate, createDateRange, createDuration, createEvents, createEventSources, createResources, hasOwn,
4
+ isArray, isFunction, isPlainObject, keys, setMidnight
5
5
  } from '#lib';
6
- import {SvelteMap} from "svelte/reactivity";
6
+ import {objectProxy} from './proxy.svelte.js';
7
7
 
8
8
  function createOptions(plugins) {
9
9
  let options = {
@@ -132,7 +132,7 @@ function createParsers(plugins) {
132
132
  // Options where default value is passed to the function
133
133
  const specialOptions = ['buttonText', 'customButtons', 'theme'];
134
134
 
135
- export function optionsState(mainState, plugins, userOptions) {
135
+ export function optionsState(plugins, userOptions) {
136
136
  // Create default options and parsers
137
137
  let defOptions = createOptions(plugins);
138
138
  let parsers = createParsers(plugins);
@@ -142,84 +142,73 @@ export function optionsState(mainState, plugins, userOptions) {
142
142
  userOptions = parseOptions(userOptions, parsers);
143
143
 
144
144
  // Extract view-specific options
145
- let defViewsOptions = extractOption(defOptions, 'views') ?? {};
146
- let userViewsOptions = extractOption(userOptions, 'views') ?? {};
145
+ let defViews = extractOption(defOptions, 'views') ?? {};
146
+ let userViews = extractOption(userOptions, 'views') ?? {};
147
147
 
148
148
  // Create options state
149
- let options = new SvelteMap();
150
- setOptions(options, defOptions);
149
+ let options = objectProxy({});
150
+ assign(options, defOptions);
151
+ // Set initial view based on input
152
+ if (userOptions.view) {
153
+ options.view = userOptions.view;
154
+ }
151
155
 
156
+ // Set options for each view
152
157
  let setters = {};
153
- let currentOpts = {};
154
-
155
- function initEffects() {
156
- // Set initial view based on input
157
- if (userOptions.view) {
158
- options.set('view', userOptions.view);
159
- }
160
- // Set options for each view
161
- let views = new Set([...keys(defViewsOptions), ...keys(userViewsOptions)]);
162
- for (let view of views) {
163
- let userViewOptions = userViewsOptions[view] ?? {};
164
- let defOpts = mergeOpts(defOptions, defViewsOptions[view] ?? defViewsOptions[userViewOptions.type] ?? {});
165
- let opts = mergeOpts(defOpts, userOptions, userViewOptions);
166
- let component = extractOption(opts, 'component');
167
- // View has been set
168
- delete opts.view;
169
- // Set up option setters and delete unknown options
170
- for (let key of keys(opts)) {
171
- if (options.has(key)) {
172
- if (!setters[key]) {
173
- setters[key] = [];
174
- }
175
- setters[key].push(
176
- specialOptions.includes(key)
177
- ? value => opts[key] = isFunction(value) ? value(defOpts[key]) : value
178
- : value => opts[key] = value
179
- );
180
- } else {
181
- delete opts[key];
158
+ let viewOptions = {};
159
+ let viewComponents = {};
160
+ let views = new Set([...keys(defViews), ...keys(userViews)]);
161
+ for (let view of views) {
162
+ let userViewOptions = userViews[view] ?? {};
163
+ let defOpts = mergeOpts(defOptions, defViews[view] ?? defViews[userViewOptions.type] ?? {});
164
+ let opts = mergeOpts(defOpts, userOptions, userViewOptions);
165
+ let component = extractOption(opts, 'component');
166
+ // View has been set
167
+ delete opts.view;
168
+ // Set up option setters and delete unknown options
169
+ for (let key of keys(opts)) {
170
+ if (hasOwn(options, key)) {
171
+ if (!setters[key]) {
172
+ setters[key] = [];
182
173
  }
174
+ setters[key].push(
175
+ specialOptions.includes(key)
176
+ ? value => opts[key] = isFunction(value) ? value(defOpts[key]) : value
177
+ : value => opts[key] = value
178
+ );
179
+ } else {
180
+ delete opts[key];
183
181
  }
184
- // When view changes...
185
- $effect.pre(() => {
186
- let newView = options.get('view');
187
- untrack(() => {
188
- if (newView === view) {
189
- // ...switch view component
190
- mainState.setViewComponent(component);
191
- // ...and update options
192
- currentOpts = opts;
193
- setOptions(options, opts);
194
- }
195
- });
196
- });
197
182
  }
183
+ viewOptions[view] = opts;
184
+ viewComponents[view] = component;
198
185
  }
199
186
 
200
- return {
201
- proxy: new Proxy(options, {
202
- set(options, key, value) {
203
- currentOpts[key] = value;
204
- options.set(key, value);
205
- return true;
206
- },
207
- get(options, key) {
208
- return options.get(key);
209
- }
210
- }),
211
- setOption(key, value, parsed) {
212
- if (options.has(key)) {
213
- if (!parsed && key in parsers) {
214
- value = parsers[key](value);
187
+ assign(options, viewOptions[options.view]);
188
+
189
+ return [
190
+ options,
191
+ function setOption(key, value, parsed = true) {
192
+ if (hasOwn(options, key)) {
193
+ if (!parsed) {
194
+ if (key in parsers) {
195
+ value = parsers[key](value);
196
+ } else if (isPlainObject(value)) {
197
+ value = {...value};
198
+ } else if (isArray(value)) {
199
+ value = [...value];
200
+ }
215
201
  }
216
202
  // Set value for all views
217
203
  setters[key]?.forEach(set => set(value));
218
- options.set(key, currentOpts[key] ?? value);
204
+ options[key] = value;
219
205
  }
220
206
  },
221
- initEffects
222
- };
207
+ function setViewOptions(view) {
208
+ assign(options, viewOptions[view]);
209
+ return viewComponents[view];
210
+ }
211
+ ];
223
212
  }
224
213
 
225
214
  function parseOptions(opts, parsers) {
@@ -262,12 +251,6 @@ function mergeOpts(...args) {
262
251
  return result;
263
252
  }
264
253
 
265
- function setOptions(map, options) {
266
- for (let [key, value] of entries(options)) {
267
- map.set(key, value);
268
- }
269
- }
270
-
271
254
  export function diff(options, prevOptions) {
272
255
  let diff = [];
273
256
  for (let key of keys(options)) {
@@ -0,0 +1,48 @@
1
+ import {hasOwn} from '#lib';
2
+
3
+ /**
4
+ * Array proxy that triggers the effect on any assignment to first-level elements or length property
5
+ */
6
+ export function arrayProxy(array) {
7
+ let counter = 0;
8
+ let version = $state(counter);
9
+ return proxy(
10
+ array,
11
+ () => version,
12
+ () => true,
13
+ () => version = ++counter
14
+ );
15
+ }
16
+
17
+ /**
18
+ * Object proxy that triggers the effect on changes to first-level properties
19
+ */
20
+ export function objectProxy(object) {
21
+ let counter = 0;
22
+ let versions = $state({});
23
+ return proxy(
24
+ object,
25
+ prop => versions[prop],
26
+ (a, b) => a !== b,
27
+ prop => versions[prop] = ++counter
28
+ );
29
+ }
30
+
31
+ function proxy(target, setDependency, hasEffect, invokeEffect) {
32
+ return new Proxy(target, {
33
+ get(target, prop, receiver) {
34
+ if (hasOwn(target, prop)) {
35
+ setDependency(prop);
36
+ }
37
+ return Reflect.get(target, prop, receiver);
38
+ },
39
+ set(target, prop, value, receiver) {
40
+ let has = hasEffect(target[prop], value);
41
+ let result = Reflect.set(target, prop, value, receiver);
42
+ if (has) {
43
+ invokeEffect(prop);
44
+ }
45
+ return result;
46
+ }
47
+ });
48
+ }
@@ -1,31 +1,33 @@
1
1
  import {SvelteMap} from 'svelte/reactivity';
2
2
  import {createDate, identity, intl, intlRange, setMidnight} from '#lib';
3
- import {optionsState} from './options.svelte.js';
3
+ import {optionsState} from './options.js';
4
4
  import {
5
- createLoadingInvoker, loadEvents, loadResources, runDatesSet, runEventAllUpdated, runViewDidMount, setNowAndToday
5
+ createLoadingInvoker, loadEvents, loadResources, runDatesSet, runEventAllUpdated, runViewDidMount, setNowAndToday,
6
+ switchView
6
7
  } from './effects.js';
7
8
  import {activeRange, currentRange, filteredEvents, view, viewDates, viewTitle} from './derived.js';
9
+ import {arrayProxy} from './proxy.svelte.js';
8
10
 
9
11
  export default class State {
10
12
 
11
- #setOption;
13
+ options;
14
+ setOption;
15
+ setViewOptions;
12
16
 
13
17
  constructor(plugins, options) {
14
18
  // Create options state
15
- let {proxy, setOption, initEffects} = optionsState(this, plugins, options);
16
- this.options = proxy;
17
- this.#setOption = setOption;
19
+ ([this.options, this.setOption, this.setViewOptions] = optionsState(plugins, options));
18
20
 
19
21
  // Create other states
20
22
  this.auxComponents = $state([]);
21
23
  this.currentRange = $derived.by(currentRange(this));
22
24
  this.activeRange = $derived.by(activeRange(this));
23
25
  this.fetchedRange = $state({events: {}, resources: {}});
24
- this.events = $state.raw([]);
26
+ this.events = $state.raw(arrayProxy(this.options.events));
25
27
  this.filteredEvents = $derived.by(filteredEvents(this));
26
28
  this.mainEl = $state();
27
29
  this.now = $state(createDate());
28
- this.resources = $state.raw([]);
30
+ this.resources = $state.raw(arrayProxy(this.options.resources));
29
31
  this.today = $state(setMidnight(createDate()));
30
32
  this.intlEventTime = $derived.by(intlRange(this, 'eventTimeFormat'));
31
33
  this.intlDayHeader = $derived.by(intl(this, 'dayHeaderFormat'));
@@ -48,12 +50,12 @@ export default class State {
48
50
  plugin.initState?.(this);
49
51
  }
50
52
 
51
- initEffects();
52
53
  this.#initEffects();
53
54
  }
54
55
 
55
56
  #initEffects() {
56
- let loading = createLoadingInvoker(this.options);
57
+ let loading = createLoadingInvoker(this);
58
+ $effect.pre(switchView(this));
57
59
  $effect.pre(setNowAndToday(this));
58
60
  $effect(loadEvents(this, loading));
59
61
  $effect(loadResources(this, loading));
@@ -61,14 +63,4 @@ export default class State {
61
63
  $effect(runEventAllUpdated(this));
62
64
  $effect(runViewDidMount(this));
63
65
  }
64
-
65
- setViewComponent(component) {
66
- this.extensions = {};
67
- this.features = [];
68
- this.viewComponent = component(this);
69
- }
70
-
71
- setOption(name, value, parsed = true) {
72
- this.#setOption(name, value, parsed);
73
- }
74
66
  }