UNPKG

svelte-gantt

Version:

Interactive JavaScript Gantt chart/resource booking component

1,846 lines (1,713 loc) 267 kB
/** @returns {void} */ function noop() {} /** * @template T * @template S * @param {T} tar * @param {S} src * @returns {T & S} */ function assign(tar, src) { // @ts-ignore for (const k in src) tar[k] = src[k]; return /** @type {T & S} */ (tar); } function run(fn) { return fn(); } function blank_object() { return Object.create(null); } /** * @param {Function[]} fns * @returns {void} */ function run_all(fns) { fns.forEach(run); } /** * @param {any} thing * @returns {thing is Function} */ function is_function(thing) { return typeof thing === 'function'; } /** @returns {boolean} */ function safe_not_equal(a, b) { return a != a ? b == b : a !== b || (a && typeof a === 'object') || typeof a === 'function'; } let src_url_equal_anchor; /** * @param {string} element_src * @param {string} url * @returns {boolean} */ function src_url_equal(element_src, url) { if (element_src === url) return true; if (!src_url_equal_anchor) { src_url_equal_anchor = document.createElement('a'); } // This is actually faster than doing URL(..).href src_url_equal_anchor.href = url; return element_src === src_url_equal_anchor.href; } /** @returns {boolean} */ function is_empty(obj) { return Object.keys(obj).length === 0; } function subscribe(store, ...callbacks) { if (store == null) { for (const callback of callbacks) { callback(undefined); } return noop; } const unsub = store.subscribe(...callbacks); return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub; } /** @returns {void} */ function component_subscribe(component, store, callback) { component.$$.on_destroy.push(subscribe(store, callback)); } function create_slot(definition, ctx, $$scope, fn) { if (definition) { const slot_ctx = get_slot_context(definition, ctx, $$scope, fn); return definition[0](slot_ctx); } } function get_slot_context(definition, ctx, $$scope, fn) { return definition[1] && fn ? assign($$scope.ctx.slice(), definition[1](fn(ctx))) : $$scope.ctx; } function get_slot_changes(definition, $$scope, dirty, fn) { if (definition[2] && fn) { const lets = definition[2](fn(dirty)); if ($$scope.dirty === undefined) { return lets; } if (typeof lets === 'object') { const merged = []; const len = Math.max($$scope.dirty.length, lets.length); for (let i = 0; i < len; i += 1) { merged[i] = $$scope.dirty[i] | lets[i]; } return merged; } return $$scope.dirty | lets; } return $$scope.dirty; } /** @returns {void} */ function update_slot_base( slot, slot_definition, ctx, $$scope, slot_changes, get_slot_context_fn ) { if (slot_changes) { const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn); slot.p(slot_context, slot_changes); } } /** @returns {any[] | -1} */ function get_all_dirty_from_scope($$scope) { if ($$scope.ctx.length > 32) { const dirty = []; const length = $$scope.ctx.length / 32; for (let i = 0; i < length; i++) { dirty[i] = -1; } return dirty; } return -1; } /** @returns {{}} */ function exclude_internal_props(props) { const result = {}; for (const k in props) if (k[0] !== '$') result[k] = props[k]; return result; } /** @returns {{}} */ function compute_rest_props(props, keys) { const rest = {}; keys = new Set(keys); for (const k in props) if (!keys.has(k) && k[0] !== '$') rest[k] = props[k]; return rest; } function null_to_empty(value) { return value == null ? '' : value; } function set_store_value(store, ret, value) { store.set(value); return ret; } function action_destroyer(action_result) { return action_result && is_function(action_result.destroy) ? action_result.destroy : noop; } /** * @param {Node} target * @param {Node} node * @returns {void} */ function append(target, node) { target.appendChild(node); } /** * @param {Node} target * @param {Node} node * @param {Node} [anchor] * @returns {void} */ function insert(target, node, anchor) { target.insertBefore(node, anchor || null); } /** * @param {Node} node * @returns {void} */ function detach(node) { if (node.parentNode) { node.parentNode.removeChild(node); } } /** * @returns {void} */ function destroy_each(iterations, detaching) { for (let i = 0; i < iterations.length; i += 1) { if (iterations[i]) iterations[i].d(detaching); } } /** * @template {keyof HTMLElementTagNameMap} K * @param {K} name * @returns {HTMLElementTagNameMap[K]} */ function element(name) { return document.createElement(name); } /** * @template {keyof SVGElementTagNameMap} K * @param {K} name * @returns {SVGElement} */ function svg_element(name) { return document.createElementNS('http://www.w3.org/2000/svg', name); } /** * @param {string} data * @returns {Text} */ function text(data) { return document.createTextNode(data); } /** * @returns {Text} */ function space() { return text(' '); } /** * @returns {Text} */ function empty() { return text(''); } /** * @param {EventTarget} node * @param {string} event * @param {EventListenerOrEventListenerObject} handler * @param {boolean | AddEventListenerOptions | EventListenerOptions} [options] * @returns {() => void} */ function listen(node, event, handler, options) { node.addEventListener(event, handler, options); return () => node.removeEventListener(event, handler, options); } /** * @returns {(event: any) => any} */ function stop_propagation(fn) { return function (event) { event.stopPropagation(); // @ts-ignore return fn.call(this, event); }; } /** * @param {Element} node * @param {string} attribute * @param {string} [value] * @returns {void} */ function attr(node, attribute, value) { if (value == null) node.removeAttribute(attribute); else if (node.getAttribute(attribute) !== value) node.setAttribute(attribute, value); } /** * @param {Element} element * @returns {ChildNode[]} */ function children(element) { return Array.from(element.childNodes); } /** * @param {Text} text * @param {unknown} data * @returns {void} */ function set_data(text, data) { data = '' + data; if (text.data === data) return; text.data = /** @type {string} */ (data); } /** * @returns {void} */ function set_style(node, key, value, important) { if (value == null) { node.style.removeProperty(key); } else { node.style.setProperty(key, value, important ? 'important' : ''); } } // unfortunately this can't be a constant as that wouldn't be tree-shakeable // so we cache the result instead /** * @type {boolean} */ let crossorigin; /** * @returns {boolean} */ function is_crossorigin() { if (crossorigin === undefined) { crossorigin = false; try { if (typeof window !== 'undefined' && window.parent) { void window.parent.document; } } catch (error) { crossorigin = true; } } return crossorigin; } /** * @param {HTMLElement} node * @param {() => void} fn * @returns {() => void} */ function add_iframe_resize_listener(node, fn) { const computed_style = getComputedStyle(node); if (computed_style.position === 'static') { node.style.position = 'relative'; } const iframe = element('iframe'); iframe.setAttribute( 'style', 'display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; ' + 'overflow: hidden; border: 0; opacity: 0; pointer-events: none; z-index: -1;' ); iframe.setAttribute('aria-hidden', 'true'); iframe.tabIndex = -1; const crossorigin = is_crossorigin(); /** * @type {() => void} */ let unsubscribe; if (crossorigin) { iframe.src = "data:text/html,<script>onresize=function(){parent.postMessage(0,'*')}</script>"; unsubscribe = listen( window, 'message', /** @param {MessageEvent} event */ (event) => { if (event.source === iframe.contentWindow) fn(); } ); } else { iframe.src = 'about:blank'; iframe.onload = () => { unsubscribe = listen(iframe.contentWindow, 'resize', fn); // make sure an initial resize event is fired _after_ the iframe is loaded (which is asynchronous) // see https://github.com/sveltejs/svelte/issues/4233 fn(); }; } append(node, iframe); return () => { if (crossorigin) { unsubscribe(); } else if (unsubscribe && iframe.contentWindow) { unsubscribe(); } detach(iframe); }; } /** * @returns {void} */ function toggle_class(element, name, toggle) { // The `!!` is required because an `undefined` flag means flipping the current state. element.classList.toggle(name, !!toggle); } /** * @template T * @param {string} type * @param {T} [detail] * @param {{ bubbles?: boolean, cancelable?: boolean }} [options] * @returns {CustomEvent<T>} */ function custom_event(type, detail, { bubbles = false, cancelable = false } = {}) { return new CustomEvent(type, { detail, bubbles, cancelable }); } /** */ class HtmlTag { /** * @private * @default false */ is_svg = false; /** parent for creating node */ e = undefined; /** html tag nodes */ n = undefined; /** target */ t = undefined; /** anchor */ a = undefined; constructor(is_svg = false) { this.is_svg = is_svg; this.e = this.n = null; } /** * @param {string} html * @returns {void} */ c(html) { this.h(html); } /** * @param {string} html * @param {HTMLElement | SVGElement} target * @param {HTMLElement | SVGElement} anchor * @returns {void} */ m(html, target, anchor = null) { if (!this.e) { if (this.is_svg) this.e = svg_element(/** @type {keyof SVGElementTagNameMap} */ (target.nodeName)); /** #7364 target for <template> may be provided as #document-fragment(11) */ else this.e = element( /** @type {keyof HTMLElementTagNameMap} */ ( target.nodeType === 11 ? 'TEMPLATE' : target.nodeName ) ); this.t = target.tagName !== 'TEMPLATE' ? target : /** @type {HTMLTemplateElement} */ (target).content; this.c(html); } this.i(anchor); } /** * @param {string} html * @returns {void} */ h(html) { this.e.innerHTML = html; this.n = Array.from( this.e.nodeName === 'TEMPLATE' ? this.e.content.childNodes : this.e.childNodes ); } /** * @returns {void} */ i(anchor) { for (let i = 0; i < this.n.length; i += 1) { insert(this.t, this.n[i], anchor); } } /** * @param {string} html * @returns {void} */ p(html) { this.d(); this.h(html); this.i(this.a); } /** * @returns {void} */ d() { this.n.forEach(detach); } } function construct_svelte_component(component, props) { return new component(props); } /** * @typedef {Node & { * claim_order?: number; * hydrate_init?: true; * actual_end_child?: NodeEx; * childNodes: NodeListOf<NodeEx>; * }} NodeEx */ /** @typedef {ChildNode & NodeEx} ChildNodeEx */ /** @typedef {NodeEx & { claim_order: number }} NodeEx2 */ /** * @typedef {ChildNodeEx[] & { * claim_info?: { * last_index: number; * total_claimed: number; * }; * }} ChildNodeArray */ let current_component; /** @returns {void} */ function set_current_component(component) { current_component = component; } function get_current_component() { if (!current_component) throw new Error('Function called outside component initialization'); return current_component; } /** * The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM. * It must be called during the component's initialisation (but doesn't need to live *inside* the component; * it can be called from an external module). * * If a function is returned _synchronously_ from `onMount`, it will be called when the component is unmounted. * * `onMount` does not run inside a [server-side component](https://svelte.dev/docs#run-time-server-side-component-api). * * https://svelte.dev/docs/svelte#onmount * @template T * @param {() => import('./private.js').NotFunction<T> | Promise<import('./private.js').NotFunction<T>> | (() => any)} fn * @returns {void} */ function onMount(fn) { get_current_component().$$.on_mount.push(fn); } /** * Schedules a callback to run immediately before the component is unmounted. * * Out of `onMount`, `beforeUpdate`, `afterUpdate` and `onDestroy`, this is the * only one that runs inside a server-side component. * * https://svelte.dev/docs/svelte#ondestroy * @param {() => any} fn * @returns {void} */ function onDestroy(fn) { get_current_component().$$.on_destroy.push(fn); } /** * Creates an event dispatcher that can be used to dispatch [component events](https://svelte.dev/docs#template-syntax-component-directives-on-eventname). * Event dispatchers are functions that can take two arguments: `name` and `detail`. * * Component events created with `createEventDispatcher` create a * [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). * These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture). * The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) * property and can contain any type of data. * * The event dispatcher can be typed to narrow the allowed event names and the type of the `detail` argument: * ```ts * const dispatch = createEventDispatcher<{ * loaded: never; // does not take a detail argument * change: string; // takes a detail argument of type string, which is required * optional: number | null; // takes an optional detail argument of type number * }>(); * ``` * * https://svelte.dev/docs/svelte#createeventdispatcher * @template {Record<string, any>} [EventMap=any] * @returns {import('./public.js').EventDispatcher<EventMap>} */ function createEventDispatcher() { const component = get_current_component(); return (type, detail, { cancelable = false } = {}) => { const callbacks = component.$$.callbacks[type]; if (callbacks) { // TODO are there situations where events could be dispatched // in a server (non-DOM) environment? const event = custom_event(/** @type {string} */ (type), detail, { cancelable }); callbacks.slice().forEach((fn) => { fn.call(component, event); }); return !event.defaultPrevented; } return true; }; } /** * Associates an arbitrary `context` object with the current component and the specified `key` * and returns that object. The context is then available to children of the component * (including slotted content) with `getContext`. * * Like lifecycle functions, this must be called during component initialisation. * * https://svelte.dev/docs/svelte#setcontext * @template T * @param {any} key * @param {T} context * @returns {T} */ function setContext(key, context) { get_current_component().$$.context.set(key, context); return context; } /** * Retrieves the context that belongs to the closest parent component with the specified `key`. * Must be called during component initialisation. * * https://svelte.dev/docs/svelte#getcontext * @template T * @param {any} key * @returns {T} */ function getContext(key) { return get_current_component().$$.context.get(key); } // TODO figure out if we still want to support // shorthand events, or if we want to implement // a real bubbling mechanism /** * @param component * @param event * @returns {void} */ function bubble(component, event) { const callbacks = component.$$.callbacks[event.type]; if (callbacks) { // @ts-ignore callbacks.slice().forEach((fn) => fn.call(this, event)); } } const dirty_components = []; const binding_callbacks = []; let render_callbacks = []; const flush_callbacks = []; const resolved_promise = /* @__PURE__ */ Promise.resolve(); let update_scheduled = false; /** @returns {void} */ function schedule_update() { if (!update_scheduled) { update_scheduled = true; resolved_promise.then(flush); } } /** @returns {Promise<void>} */ function tick() { schedule_update(); return resolved_promise; } /** @returns {void} */ function add_render_callback(fn) { render_callbacks.push(fn); } // flush() calls callbacks in this order: // 1. All beforeUpdate callbacks, in order: parents before children // 2. All bind:this callbacks, in reverse order: children before parents. // 3. All afterUpdate callbacks, in order: parents before children. EXCEPT // for afterUpdates called during the initial onMount, which are called in // reverse order: children before parents. // Since callbacks might update component values, which could trigger another // call to flush(), the following steps guard against this: // 1. During beforeUpdate, any updated components will be added to the // dirty_components array and will cause a reentrant call to flush(). Because // the flush index is kept outside the function, the reentrant call will pick // up where the earlier call left off and go through all dirty components. The // current_component value is saved and restored so that the reentrant call will // not interfere with the "parent" flush() call. // 2. bind:this callbacks cannot trigger new flush() calls. // 3. During afterUpdate, any updated components will NOT have their afterUpdate // callback called a second time; the seen_callbacks set, outside the flush() // function, guarantees this behavior. const seen_callbacks = new Set(); let flushidx = 0; // Do *not* move this inside the flush() function /** @returns {void} */ function flush() { // Do not reenter flush while dirty components are updated, as this can // result in an infinite loop. Instead, let the inner flush handle it. // Reentrancy is ok afterwards for bindings etc. if (flushidx !== 0) { return; } const saved_component = current_component; do { // first, call beforeUpdate functions // and update components try { while (flushidx < dirty_components.length) { const component = dirty_components[flushidx]; flushidx++; set_current_component(component); update(component.$$); } } catch (e) { // reset dirty state to not end up in a deadlocked state and then rethrow dirty_components.length = 0; flushidx = 0; throw e; } set_current_component(null); dirty_components.length = 0; flushidx = 0; while (binding_callbacks.length) binding_callbacks.pop()(); // then, once components are updated, call // afterUpdate functions. This may cause // subsequent updates... for (let i = 0; i < render_callbacks.length; i += 1) { const callback = render_callbacks[i]; if (!seen_callbacks.has(callback)) { // ...so guard against infinite loops seen_callbacks.add(callback); callback(); } } render_callbacks.length = 0; } while (dirty_components.length); while (flush_callbacks.length) { flush_callbacks.pop()(); } update_scheduled = false; seen_callbacks.clear(); set_current_component(saved_component); } /** @returns {void} */ function update($$) { if ($$.fragment !== null) { $$.update(); run_all($$.before_update); const dirty = $$.dirty; $$.dirty = [-1]; $$.fragment && $$.fragment.p($$.ctx, dirty); $$.after_update.forEach(add_render_callback); } } /** * Useful for example to execute remaining `afterUpdate` callbacks before executing `destroy`. * @param {Function[]} fns * @returns {void} */ function flush_render_callbacks(fns) { const filtered = []; const targets = []; render_callbacks.forEach((c) => (fns.indexOf(c) === -1 ? filtered.push(c) : targets.push(c))); targets.forEach((c) => c()); render_callbacks = filtered; } const outroing = new Set(); /** * @type {Outro} */ let outros; /** * @returns {void} */ function group_outros() { outros = { r: 0, c: [], p: outros // parent group }; } /** * @returns {void} */ function check_outros() { if (!outros.r) { run_all(outros.c); } outros = outros.p; } /** * @param {import('./private.js').Fragment} block * @param {0 | 1} [local] * @returns {void} */ function transition_in(block, local) { if (block && block.i) { outroing.delete(block); block.i(local); } } /** * @param {import('./private.js').Fragment} block * @param {0 | 1} local * @param {0 | 1} [detach] * @param {() => void} [callback] * @returns {void} */ function transition_out(block, local, detach, callback) { if (block && block.o) { if (outroing.has(block)) return; outroing.add(block); outros.c.push(() => { outroing.delete(block); if (callback) { if (detach) block.d(1); callback(); } }); block.o(local); } else if (callback) { callback(); } } /** @typedef {1} INTRO */ /** @typedef {0} OUTRO */ /** @typedef {{ direction: 'in' | 'out' | 'both' }} TransitionOptions */ /** @typedef {(node: Element, params: any, options: TransitionOptions) => import('../transition/public.js').TransitionConfig} TransitionFn */ /** * @typedef {Object} Outro * @property {number} r * @property {Function[]} c * @property {Object} p */ /** * @typedef {Object} PendingProgram * @property {number} start * @property {INTRO|OUTRO} b * @property {Outro} [group] */ /** * @typedef {Object} Program * @property {number} a * @property {INTRO|OUTRO} b * @property {1|-1} d * @property {number} duration * @property {number} start * @property {number} end * @property {Outro} [group] */ // general each functions: function ensure_array_like(array_like_or_iterator) { return array_like_or_iterator?.length !== undefined ? array_like_or_iterator : Array.from(array_like_or_iterator); } /** @returns {void} */ function outro_and_destroy_block(block, lookup) { transition_out(block, 1, 1, () => { lookup.delete(block.key); }); } /** @returns {any[]} */ function update_keyed_each( old_blocks, dirty, get_key, dynamic, ctx, list, lookup, node, destroy, create_each_block, next, get_context ) { let o = old_blocks.length; let n = list.length; let i = o; const old_indexes = {}; while (i--) old_indexes[old_blocks[i].key] = i; const new_blocks = []; const new_lookup = new Map(); const deltas = new Map(); const updates = []; i = n; while (i--) { const child_ctx = get_context(ctx, list, i); const key = get_key(child_ctx); let block = lookup.get(key); if (!block) { block = create_each_block(key, child_ctx); block.c(); } else if (dynamic) { // defer updates until all the DOM shuffling is done updates.push(() => block.p(child_ctx, dirty)); } new_lookup.set(key, (new_blocks[i] = block)); if (key in old_indexes) deltas.set(key, Math.abs(i - old_indexes[key])); } const will_move = new Set(); const did_move = new Set(); /** @returns {void} */ function insert(block) { transition_in(block, 1); block.m(node, next); lookup.set(block.key, block); next = block.first; n--; } while (o && n) { const new_block = new_blocks[n - 1]; const old_block = old_blocks[o - 1]; const new_key = new_block.key; const old_key = old_block.key; if (new_block === old_block) { // do nothing next = new_block.first; o--; n--; } else if (!new_lookup.has(old_key)) { // remove old block destroy(old_block, lookup); o--; } else if (!lookup.has(new_key) || will_move.has(new_key)) { insert(new_block); } else if (did_move.has(old_key)) { o--; } else if (deltas.get(new_key) > deltas.get(old_key)) { did_move.add(new_key); insert(new_block); } else { will_move.add(old_key); o--; } } while (o--) { const old_block = old_blocks[o]; if (!new_lookup.has(old_block.key)) destroy(old_block, lookup); } while (n) insert(new_blocks[n - 1]); run_all(updates); return new_blocks; } /** @returns {{}} */ function get_spread_update(levels, updates) { const update = {}; const to_null_out = {}; const accounted_for = { $$scope: 1 }; let i = levels.length; while (i--) { const o = levels[i]; const n = updates[i]; if (n) { for (const key in o) { if (!(key in n)) to_null_out[key] = 1; } for (const key in n) { if (!accounted_for[key]) { update[key] = n[key]; accounted_for[key] = 1; } } levels[i] = n; } else { for (const key in o) { accounted_for[key] = 1; } } } for (const key in to_null_out) { if (!(key in update)) update[key] = undefined; } return update; } function get_spread_object(spread_props) { return typeof spread_props === 'object' && spread_props !== null ? spread_props : {}; } /** @returns {void} */ function create_component(block) { block && block.c(); } /** @returns {void} */ function mount_component(component, target, anchor) { const { fragment, after_update } = component.$$; fragment && fragment.m(target, anchor); // onMount happens before the initial afterUpdate add_render_callback(() => { const new_on_destroy = component.$$.on_mount.map(run).filter(is_function); // if the component was destroyed immediately // it will update the `$$.on_destroy` reference to `null`. // the destructured on_destroy may still reference to the old array if (component.$$.on_destroy) { component.$$.on_destroy.push(...new_on_destroy); } else { // Edge case - component was destroyed immediately, // most likely as a result of a binding initialising run_all(new_on_destroy); } component.$$.on_mount = []; }); after_update.forEach(add_render_callback); } /** @returns {void} */ function destroy_component(component, detaching) { const $$ = component.$$; if ($$.fragment !== null) { flush_render_callbacks($$.after_update); run_all($$.on_destroy); $$.fragment && $$.fragment.d(detaching); // TODO null out other refs, including component.$$ (but need to // preserve final state?) $$.on_destroy = $$.fragment = null; $$.ctx = []; } } /** @returns {void} */ function make_dirty(component, i) { if (component.$$.dirty[0] === -1) { dirty_components.push(component); schedule_update(); component.$$.dirty.fill(0); } component.$$.dirty[(i / 31) | 0] |= 1 << i % 31; } // TODO: Document the other params /** * @param {SvelteComponent} component * @param {import('./public.js').ComponentConstructorOptions} options * * @param {import('./utils.js')['not_equal']} not_equal Used to compare props and state values. * @param {(target: Element | ShadowRoot) => void} [append_styles] Function that appends styles to the DOM when the component is first initialised. * This will be the `add_css` function from the compiled component. * * @returns {void} */ function init( component, options, instance, create_fragment, not_equal, props, append_styles = null, dirty = [-1] ) { const parent_component = current_component; set_current_component(component); /** @type {import('./private.js').T$$} */ const $$ = (component.$$ = { fragment: null, ctx: [], // state props, update: noop, not_equal, bound: blank_object(), // lifecycle on_mount: [], on_destroy: [], on_disconnect: [], before_update: [], after_update: [], context: new Map(options.context || (parent_component ? parent_component.$$.context : [])), // everything else callbacks: blank_object(), dirty, skip_bound: false, root: options.target || parent_component.$$.root }); append_styles && append_styles($$.root); let ready = false; $$.ctx = instance ? instance(component, options.props || {}, (i, ret, ...rest) => { const value = rest.length ? rest[0] : ret; if ($$.ctx && not_equal($$.ctx[i], ($$.ctx[i] = value))) { if (!$$.skip_bound && $$.bound[i]) $$.bound[i](value); if (ready) make_dirty(component, i); } return ret; }) : []; $$.update(); ready = true; run_all($$.before_update); // `false` as a special case of no DOM component $$.fragment = create_fragment ? create_fragment($$.ctx) : false; if (options.target) { if (options.hydrate) { // TODO: what is the correct type here? // @ts-expect-error const nodes = children(options.target); $$.fragment && $$.fragment.l(nodes); nodes.forEach(detach); } else { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion $$.fragment && $$.fragment.c(); } if (options.intro) transition_in(component.$$.fragment); mount_component(component, options.target, options.anchor); flush(); } set_current_component(parent_component); } /** * Base class for Svelte components. Used when dev=false. * * @template {Record<string, any>} [Props=any] * @template {Record<string, any>} [Events=any] */ class SvelteComponent { /** * ### PRIVATE API * * Do not use, may change at any time * * @type {any} */ $$ = undefined; /** * ### PRIVATE API * * Do not use, may change at any time * * @type {any} */ $$set = undefined; /** @returns {void} */ $destroy() { destroy_component(this, 1); this.$destroy = noop; } /** * @template {Extract<keyof Events, string>} K * @param {K} type * @param {((e: Events[K]) => void) | null | undefined} callback * @returns {() => void} */ $on(type, callback) { if (!is_function(callback)) { return noop; } const callbacks = this.$$.callbacks[type] || (this.$$.callbacks[type] = []); callbacks.push(callback); return () => { const index = callbacks.indexOf(callback); if (index !== -1) callbacks.splice(index, 1); }; } /** * @param {Partial<Props>} props * @returns {void} */ $set(props) { if (this.$$set && !is_empty(props)) { this.$$.skip_bound = true; this.$$set(props); this.$$.skip_bound = false; } } } /** * @typedef {Object} CustomElementPropDefinition * @property {string} [attribute] * @property {boolean} [reflect] * @property {'String'|'Boolean'|'Number'|'Array'|'Object'} [type] */ // generated during release, do not modify const PUBLIC_VERSION = '4'; if (typeof window !== 'undefined') // @ts-ignore (window.__svelte || (window.__svelte = { v: new Set() })).v.add(PUBLIC_VERSION); const subscriber_queue = []; /** * Creates a `Readable` store that allows reading by subscription. * * https://svelte.dev/docs/svelte-store#readable * @template T * @param {T} [value] initial value * @param {import('./public.js').StartStopNotifier<T>} [start] * @returns {import('./public.js').Readable<T>} */ function readable(value, start) { return { subscribe: writable(value, start).subscribe }; } /** * Create a `Writable` store that allows both updating and reading by subscription. * * https://svelte.dev/docs/svelte-store#writable * @template T * @param {T} [value] initial value * @param {import('./public.js').StartStopNotifier<T>} [start] * @returns {import('./public.js').Writable<T>} */ function writable(value, start = noop) { /** @type {import('./public.js').Unsubscriber} */ let stop; /** @type {Set<import('./private.js').SubscribeInvalidateTuple<T>>} */ const subscribers = new Set(); /** @param {T} new_value * @returns {void} */ function set(new_value) { if (safe_not_equal(value, new_value)) { value = new_value; if (stop) { // store is ready const run_queue = !subscriber_queue.length; for (const subscriber of subscribers) { subscriber[1](); subscriber_queue.push(subscriber, value); } if (run_queue) { for (let i = 0; i < subscriber_queue.length; i += 2) { subscriber_queue[i][0](subscriber_queue[i + 1]); } subscriber_queue.length = 0; } } } } /** * @param {import('./public.js').Updater<T>} fn * @returns {void} */ function update(fn) { set(fn(value)); } /** * @param {import('./public.js').Subscriber<T>} run * @param {import('./private.js').Invalidator<T>} [invalidate] * @returns {import('./public.js').Unsubscriber} */ function subscribe(run, invalidate = noop) { /** @type {import('./private.js').SubscribeInvalidateTuple<T>} */ const subscriber = [run, invalidate]; subscribers.add(subscriber); if (subscribers.size === 1) { stop = start(set, update) || noop; } run(value); return () => { subscribers.delete(subscriber); if (subscribers.size === 0 && stop) { stop(); stop = null; } }; } return { set, update, subscribe }; } /** * Derived value store by synchronizing one or more readable stores and * applying an aggregation function over its input values. * * https://svelte.dev/docs/svelte-store#derived * @template {import('./private.js').Stores} S * @template T * @overload * @param {S} stores - input stores * @param {(values: import('./private.js').StoresValues<S>, set: (value: T) => void, update: (fn: import('./public.js').Updater<T>) => void) => import('./public.js').Unsubscriber | void} fn - function callback that aggregates the values * @param {T} [initial_value] - initial value * @returns {import('./public.js').Readable<T>} */ /** * Derived value store by synchronizing one or more readable stores and * applying an aggregation function over its input values. * * https://svelte.dev/docs/svelte-store#derived * @template {import('./private.js').Stores} S * @template T * @overload * @param {S} stores - input stores * @param {(values: import('./private.js').StoresValues<S>) => T} fn - function callback that aggregates the values * @param {T} [initial_value] - initial value * @returns {import('./public.js').Readable<T>} */ /** * @template {import('./private.js').Stores} S * @template T * @param {S} stores * @param {Function} fn * @param {T} [initial_value] * @returns {import('./public.js').Readable<T>} */ function derived(stores, fn, initial_value) { const single = !Array.isArray(stores); /** @type {Array<import('./public.js').Readable<any>>} */ const stores_array = single ? [stores] : stores; if (!stores_array.every(Boolean)) { throw new Error('derived() expects stores as input, got a falsy value'); } const auto = fn.length < 2; return readable(initial_value, (set, update) => { let started = false; const values = []; let pending = 0; let cleanup = noop; const sync = () => { if (pending) { return; } cleanup(); const result = fn(single ? values[0] : values, set, update); if (auto) { set(result); } else { cleanup = is_function(result) ? result : noop; } }; const unsubscribers = stores_array.map((store, i) => subscribe( store, (value) => { values[i] = value; pending &= ~(1 << i); if (started) { sync(); } }, () => { pending |= 1 << i; } ) ); started = true; sync(); return function stop() { run_all(unsubscribers); cleanup(); // We need to set this to false because callbacks can still happen despite having unsubscribed: // Callbacks might already be placed in the queue which doesn't know it should no longer // invoke this derived store. started = false; }; }); } function createEntityStore() { const { subscribe, set, update } = writable({ ids: [], entities: {} }); return { set, _update: update, subscribe, add: (item) => update(({ ids, entities }) => ({ ids: [...ids, item.model.id], entities: { ...entities, [item.model.id]: item } })), delete: (id) => update(state => { const { [id]: _, ...entities } = state.entities; return { ids: state.ids.filter(i => i !== id), entities }; }), deleteAll: (ids) => update(state => { const entities = { ...state.entities }; const idSet = new Set(ids); for (let i = 0; i < state.ids.length; i++) { if (idSet.has(state.ids[i])) { delete entities[state.ids[i]]; } } return { ids: state.ids.filter(i => !idSet.has(i)), entities }; }), update: (item) => update(({ ids, entities }) => ({ ids, entities: { ...entities, [item.model.id]: item } })), upsert: (item) => update(({ ids, entities }) => { const hasIndex = ids.indexOf(item.model.id) !== -1; return { ids: hasIndex ? ids : [...ids, item.model.id], entities: { ...entities, [item.model.id]: item } }; }), upsertAll: (items) => update(state => { const entities = { ...state.entities }; const ids = [...state.ids]; for (let i = 0; i < items.length; i++) { if (ids.indexOf(items[i].model.id) === -1) { ids.push(items[i].model.id); } entities[items[i].model.id] = items[i]; } return { ids, entities }; }), addAll: (items) => { const ids = []; const entities = {}; for (let i = 0; i < items.length; i++) { ids.push(items[i].model.id); entities[items[i].model.id] = items[i]; } set({ ids, entities }); }, refresh: () => update(store => ({ ...store })) }; } function all(store) { return derived(store, ({ ids, entities }) => { const results = []; for (let i = 0; i < ids.length; i++) { results.push(entities[ids[i]]); } return results; }); } function createDataStore() { const taskStore = createEntityStore(); const rowStore = createEntityStore(); const timeRangeStore = createEntityStore(); const allTasks = all(taskStore); const allRows = all(rowStore); const allTimeRanges = all(timeRangeStore); const rowTaskCache = derived(allTasks, $allTasks => { const cache = {}; for (let i = 0; i < $allTasks.length; i++) { const task = $allTasks[i]; if (!cache[task.model.resourceId]) { cache[task.model.resourceId] = []; } cache[task.model.resourceId].push(task.model.id); } return cache; }); return { taskStore, rowStore, timeRangeStore, allTasks, allRows, allTimeRanges, rowTaskCache }; } function isLeftClick(event) { return event.which === 1; } /** * Gets mouse position within an element * @param node * @param event */ function getRelativePos(node, event) { const rect = node.getBoundingClientRect(); const x = event.clientX - rect.left; //x position within the element. const y = event.clientY - rect.top; //y position within the element. return { x: x, y: y }; } function getRelativePosition(node, event) { const rect = node.getBoundingClientRect(); const x = event.clientX - rect.left; //x position within the element. const y = event.clientY - rect.top; //y position within the element. return [x, y]; } /** * Adds an event listener that triggers once. * @param target * @param type * @param listener * @param addOptions * @param removeOptions */ function addEventListenerOnce(target, type, listener, addOptions, removeOptions) { target.addEventListener(type, function fn() { target.removeEventListener(type, fn, removeOptions); listener.apply(this, arguments, addOptions); }); } /** * Sets the cursor on an element. Globally by default. * @param cursor * @param node */ function setCursor(cursor, node = document.body) { node.style.cursor = cursor; } function normalizeClassAttr(classes) { if (!classes) { return ''; } if (typeof classes === 'string') { return classes; } if (Array.isArray(classes)) { return classes.join(' '); } return ''; } function throttle(func, limit) { let wait = false; return function () { if (!wait) { func.apply(null, arguments); wait = true; setTimeout(function () { wait = false; }, limit); } }; } /** How much pixels near the bounds user has to drag to start scrolling */ const DRAGGING_TO_SCROLL_TRESHOLD = 40; /** How much pixels does the view scroll when dragging */ const DRAGGING_TO_SCROLL_DELTA = 40; function outOfBounds(event, rect) { return { left: event.clientX - rect.left < 0 + DRAGGING_TO_SCROLL_TRESHOLD, top: event.clientY - rect.top < 0 + DRAGGING_TO_SCROLL_TRESHOLD, right: event.clientX - rect.left > rect.width - DRAGGING_TO_SCROLL_TRESHOLD, bottom: event.clientY - rect.top > rect.height - DRAGGING_TO_SCROLL_TRESHOLD }; } const scrollIfOutOfBounds = throttle((event, scrollable) => { // throttle the following const mainContainerRect = scrollable.getBoundingClientRect(); const bounds = outOfBounds(event, mainContainerRect); if (bounds.left || bounds.right) { // scroll left scrollable.scrollTo({ left: scrollable.scrollLeft + (bounds.left ? -DRAGGING_TO_SCROLL_DELTA : DRAGGING_TO_SCROLL_DELTA), behavior: 'smooth' }); } if (bounds.top || bounds.bottom) { // scroll top scrollable.scrollTo({ top: scrollable.scrollTop + (bounds.top ? -DRAGGING_TO_SCROLL_DELTA : DRAGGING_TO_SCROLL_DELTA), behavior: 'smooth' }); } }, 250); function getRowAtPoint(event) { const elements = document.elementsFromPoint(event.clientX, event.clientY); const rowElement = elements.find(element => !!element.getAttribute('data-row-id')); if (rowElement !== undefined) { const rowId = rowElement.getAttribute('data-row-id'); return rowId; } return null; } function whenEnterPress(callback) { return (e) => { if (e.key === 'Enter') { callback(e); } }; } function createUtils(params) { return { /** * Returns position of date on a line if from and to represent length of width * @param {*} date */ getPositionByDate(date) { return getPositionByDate(date, params.from, params.to, params.width); }, getDateByPosition(x) { return getDateByPosition(x, params.from, params.to, params.width); }, roundTo(date) { if (params.dateAdapter) { return params.dateAdapter.roundTo(date, params.magnetUnit, params.magnetOffset); } // this does not consider the timezone, rounds only to the UTC time // let value = Math.round((date - 7200000) / params.magnetDuration) * params.magnetDuration; // cases where rounding to day or timezone offset is not rounded, this won't work return null; } }; } function getPositionByDate(date, from, to, width) { if (!date) { return undefined; } const durationTo = date - from; const durationToEnd = to - from; return (durationTo / durationToEnd) * width; } function getDateByPosition(x, from, to, width) { const durationTo = (x / width) * (to - from); const dateAtPosition = from + durationTo; return dateAtPosition; } // Returns the object on the left and right in an array using the given cmp function. // The compare function defined which property of the value to compare (e.g.: c => c.left) function getIndicesOnly(input, value, comparer, strict) { let lo = -1; let hi = input.length; while (hi - lo > 1) { const mid = Math.floor((lo + hi) / 2); if (strict ? comparer(input[mid]) < value : comparer(input[mid]) <= value) { lo = mid; } else { hi = mid; } } if (!strict && input[lo] !== undefined && comparer(input[lo]) === value) { hi = lo; } return [lo, hi]; } function get(input, value, comparer, strict) { const res = getIndicesOnly(input, value, comparer, strict); return [input[res[0]], input[res[1]]]; } function isDraggable(item) { return item.draggable ?? item.enableDragging ?? true; } function isResizable(item) { return item.resizable ?? item.enableResize ?? true; } function styleInject(css, ref) { if ( ref === void 0 ) ref = {}; var insertAt = ref.insertAt; if (!css || typeof document === 'undefined') { return; } var head = document.head || document.getElementsByTagName('head')[0]; var style = document.createElement('style'); style.type = 'text/css'; if (insertAt === 'top') { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } } var css_248z$e = ".sg-label-bottom.svelte-e1wt24.svelte-e1wt24{position:absolute;top:calc(100% + 10px);color:#888}.sg-task.svelte-e1wt24.svelte-e1wt24{position:absolute;border-radius:2px;white-space:nowrap;transition:background-color 0.2s,\n opacity 0.2s;pointer-events:all;touch-action:none}.sg-task-background.svelte-e1wt24.svelte-e1wt24{position:absolute;height:100%;top:0}.sg-task-content.svelte-e1wt24.svelte-e1wt24{position:absolute;height:100%;top:0;padding-left:14px;font-size:14px;display:flex;align-items:center;justify-content:flex-start;user-select:none}.sg-task.animating.svelte-e1wt24.svelte-e1wt24:not(.moving),.sg-task--sticky.svelte-e1wt24.svelte-e1wt24:not(.moving){transition:left 0.2s,\n top 0.2s,\n transform 0.2s,\n background-color 0.2s,\n width 0.2s,\n height 0.2s}.sg-task--sticky.svelte-e1wt24>.sg-task-content.svelte-e1wt24{position:sticky;left:0;max-width:100px}.sg-task.moving.svelte-e1wt24.svelte-e1wt24{z-index:10000;opacity:0.5}.sg-task.resize-enabled.svelte-e1wt24.svelte-e1wt24:hover::before,.sg-task.resize-enabled.svelte-e1wt24.svelte-e1wt24:hover::after{content:'';width:4px;height:50%;top:25%;position:absolute;border-style:solid;border-color:var(--sg-task-resize-color);cursor:ew-resize;border-width:0 1px;z-index:1}.sg-task.resize-enabled.svelte-e1wt24.svelte-e1wt24:hover::before{margin-left:3px;left:0}.sg-task.resize-enabled.svelte-e1wt24.svelte-e1wt24:hover::after{margin-right:3px;right:0}.sg-task-reflected.svelte-e1wt24.svelte-e1wt24{opacity:0.5}.sg-task-instant.svelte-e1wt24.svelte-e1wt24{width:2px !important;margin-left:-1px}.sg-task-background.svelte-e1wt24.svelte-e1wt24{background:rgba(0, 0, 0, 0.2)}.sg-task-default{color:white;background:rgb(116, 191, 255)}.sg-task-default:hover{background:rgb(98, 161, 216)}.sg-task-default.selected{background:rgb(69, 112, 150)}.sg-task-selected{outline:2px solid var(--sg-task-selected-outline-color);outline-offset:3px;z-index:1}.sg-milestone.svelte-e1wt24.svelte-e1wt24{width:20px !important;min-width:40px;margin-left:-20px}.sg-task.sg-milestone.svelte-e1wt24.svelte-e1wt24{background:transparent}.sg-milestone.svelte-e1wt24 .sg-milestone__diamond.svelte-e1wt24{position:relative}.sg-milestone.svelte-e1wt24 .sg-milestone__diamond.svelte-e1wt24:before{position:absolute;top:0;left:50%;content:' ';height:28px;width:28px;transform-origin:0 0;transform:rotate(45deg)}.sg-milestone__diamond:before{background:rgb(116, 191, 255)}"; styleInject(css_248z$e); /* src/entities/Task.svelte generated by Svelte v4.2.19 */ function create_if_block_5$1(ctx) { let div; return { c() { div = element("div"); attr(div, "class", "sg-milestone__diamond svelte-e1wt24"); }, m(target, anchor) { insert(target, div, anchor); }, d(detaching) { if (detaching) { detach(div); } } }; } // (59:4) {#if model.amountDone} function create_if_block_4$1(ctx) { let div; return { c() { div = element("div"); attr(div, "class", "sg-task-background svelte-e1wt24"); set_style(div, "width", /*model*/ ctx[0].amountDone + "%"); }, m(target, anchor) { insert(target, div, anchor); }, p(ctx, dirty) { if (dirty & /*model*/ 1) { set_style(div, "width", /*model*/ ctx[0].amountDone + "%"); } }, d(detaching) { if (detaching) { detach(div); } } }; } // (67:8) {:else} function create_else_block$3(ctx) { let t_value = /*model*/ ctx[0].label + ""; let t; return { c() { t = text(t_value); }, m(target, anchor) { insert(target, t, anchor); }, p(ctx, dirty) { if (dirty & /*model*/ 1 && t_value !== (t_value = /*model*/ ctx[0].label + "")) set_data(t, t_value); }, d(detaching) { if (detaching) { detach(t); } } }; } // (65:30) function create_if_block_3$2(ctx) { let html_tag; let raw_value = /*taskContent*/ ctx[12](/*model*/ ctx[0]) + ""; let html_anchor; return { c() { html_tag = new HtmlTag(false); html_anchor = empty(); html_tag.a = html_anchor; }, m(target, anchor) { html_tag.m(raw_value, target, anchor); insert(target, html_anchor, anchor); }, p(ctx, dirty) { if (dirty & /*model*/ 1 && raw_value !== (raw_value = /*taskContent*/ ctx[12](/*model*/ ctx[0]) + "")) html_tag.p(raw_value); }, d(detaching) { if (detaching) { detach(html_anchor); html_tag.d(); } } }; } // (63:8) {#if model.html} function create_if_block_2$2(ctx) { let html_tag; let raw_value = /*model*/ ctx[0].html + ""; let html_anchor; return { c() { html_tag = new HtmlTag(false); html_anchor = empty(); html_tag.a = html_anchor; }, m(target, anchor) { html_tag.m(raw_value, target, anchor); insert(target, html_anchor, anchor); }, p(ctx, dirty) { if (dirty & /*model*/ 1 && raw_value !== (raw_value = /*model*/ ctx[0].html + "")) html_tag.p(raw_value); }, d(detaching) { if (detaching) { detach(html_anchor); html_tag.d(); } } }; } // (70:8) {#if model.showButton} function create_if_block_1$2(ctx) { let span; let raw_value = /*model*/ ctx[0].buttonHtml + ""; let span_class_value; let mounted; let dispose; return { c() { span = element("span"); attr(span, "class", span_class_value = "sg-task-button " + /*model*/ ctx[0].buttonClasses + " svelte-e1wt24"); attr(span, "role", "button"); attr(span, "tabi