UNPKG

@event-calendar/core

Version:

Full-sized drag & drop event calendar with resource & timeline views

264 lines (245 loc) 8 kB
import {untrack} from 'svelte'; import { assign, createDate, createDateRange, createDuration, createEvents, createEventSources, createResources, hasOwn, isArray, isFunction, isPlainObject, keys, setMidnight } from '#lib'; import {objectProxy} from './proxy.svelte.js'; function createOptions(plugins) { let options = { buttonText: { today: 'today', }, customButtons: {}, customScrollbars: false, // ec option date: new Date(), datesSet: undefined, dayHeaderFormat: { weekday: 'short', month: 'numeric', day: 'numeric' }, dayHeaderAriaLabelFormat: { dateStyle: 'full' }, displayEventEnd: true, duration: {weeks: 1}, events: [], eventAllUpdated: undefined, eventBackgroundColor: undefined, eventClassNames: undefined, eventClick: undefined, eventColor: undefined, eventContent: undefined, eventDidMount: undefined, eventFilter: undefined, // ec option eventMouseEnter: undefined, eventMouseLeave: undefined, eventOrder: undefined, eventSources: [], eventTextColor: undefined, eventTimeFormat: { hour: 'numeric', minute: '2-digit' }, filterEventsWithResources: false, firstDay: 0, headerToolbar: { start: 'title', center: '', end: 'today prev,next' }, height: undefined, hiddenDays: [], highlightedDates: [], // ec option icons: {}, // ec option lazyFetching: true, loading: undefined, locale: undefined, refetchResourcesOnNavigate: false, resources: [], selectable: false, theme: { active: 'ec-active', bgEvent: 'ec-bg-event', bgEvents: 'ec-bg-events', body: 'ec-body', button: 'ec-button', buttonGroup: 'ec-button-group', calendar: 'ec', colHead: 'ec-col-head', customScrollbars: 'ec-custom-scrollbars', day: 'ec-day', dayHead: 'ec-day-head', disabled: 'ec-disabled', event: 'ec-event', eventBody: 'ec-event-body', eventTime: 'ec-event-time', eventTitle: 'ec-event-title', events: 'ec-events', grid: 'ec-grid', header: 'ec-header', hidden: 'ec-hidden', highlight: 'ec-highlight', icon: 'ec-icon', main: 'ec-main', noBeb: 'ec-no-beb', // no block end border noIeb: 'ec-no-ieb', // no inline end border today: 'ec-today', title: 'ec-title', toolbar: 'ec-toolbar', view: '', weekdays: ['ec-sun', 'ec-mon', 'ec-tue', 'ec-wed', 'ec-thu', 'ec-fri', 'ec-sat'], }, titleFormat: { year: 'numeric', month: 'short', day: 'numeric' }, validRange: undefined, view: undefined, viewDidMount: undefined, views: {} }; // Let plugins add their options for (let plugin of plugins) { plugin.createOptions?.(options); } return options; } function createParsers(plugins) { let parsers = { date: input => setMidnight(createDate(input)), duration: createDuration, events: createEvents, eventSources: createEventSources, hiddenDays: input => [...new Set(input)], highlightedDates: input => input.map(item => setMidnight(createDate(item))), resources: input => isArray(input) ? createResources(input) : input, validRange: createDateRange }; // Let plugins add their parsers for (let plugin of plugins) { plugin.createParsers?.(parsers); } return parsers; } // Options where default value is passed to the function const specialOptions = ['buttonText', 'customButtons', 'icons', 'theme']; export function optionsState(plugins, userOptions) { // Create default options and parsers let defOptions = createOptions(plugins); let parsers = createParsers(plugins); // Parse options defOptions = parseOptions(defOptions, parsers); userOptions = parseOptions(userOptions, parsers); // Extract view-specific options let defViews = extractOption(defOptions, 'views') ?? {}; let userViews = extractOption(userOptions, 'views') ?? {}; // Create options state let options = objectProxy({}); assign(options, defOptions); // Set initial view based on input if (userOptions.view) { options.view = userOptions.view; } // Set options for each view let setters = {}; let viewOptions = {}; let viewComponents = {}; let views = new Set([...keys(defViews), ...keys(userViews)]); for (let view of views) { let userViewOptions = userViews[view] ?? {}; let defOpts = mergeOpts(defOptions, defViews[view] ?? defViews[userViewOptions.type] ?? {}); let opts = mergeOpts(defOpts, userOptions, userViewOptions); let component = extractOption(opts, 'component'); // View has been set delete opts.view; // Set up option setters and delete unknown options for (let key of keys(opts)) { if (hasOwn(options, key)) { if (!setters[key]) { setters[key] = []; } setters[key].push( specialOptions.includes(key) ? value => opts[key] = isFunction(value) ? value(defOpts[key]) : value : value => opts[key] = value ); } else { delete opts[key]; } } viewOptions[view] = opts; viewComponents[view] = component; } assign(options, viewOptions[options.view]); return [ options, function setOption(key, value, parsed = true) { if (hasOwn(options, key)) { if (!parsed) { if (key in parsers) { value = parsers[key](value); } else if (isPlainObject(value)) { value = {...value}; } else if (isArray(value)) { value = [...value]; } } // Set value for all views setters[key]?.forEach(set => set(value)); options[key] = value; } }, function setViewOptions(view) { assign(options, viewOptions[view]); return viewComponents[view]; } ]; } function parseOptions(opts, parsers) { let result = {...opts}; for (let key of keys(parsers)) { if (key in result) { result[key] = parsers[key](result[key]); } } if (opts.views) { result.views = {}; for (let view of keys(opts.views)) { result.views[view] = parseOptions(opts.views[view], parsers); } } return result; } function extractOption(options, name) { let extracted = options[name]; delete options[name]; return extracted; } function mergeOpts(...args) { let result = {}; for (let opts of args) { let override = {}; for (let key of specialOptions) { if (isFunction(opts[key])) { override[key] = opts[key](result[key]); } } result = { ...result, ...opts, ...override }; } return result; } export function diff(options, prevOptions) { let diff = []; for (let key of keys(options)) { if (options[key] !== prevOptions[key]) { diff.push([key, options[key]]); } } return diff; }