diff --git a/dist/index.js b/dist/index.js index dfad756..fb3ac3d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1100,7 +1100,7 @@ var require_react_development = __commonJS({ var dispatcher = resolveDispatcher(); return dispatcher.useRef(initialValue); } - function useEffect4(create, deps) { + function useEffect5(create, deps) { var dispatcher = resolveDispatcher(); return dispatcher.useEffect(create, deps); } @@ -1882,7 +1882,7 @@ var require_react_development = __commonJS({ exports.useContext = useContext2; exports.useDebugValue = useDebugValue2; exports.useDeferredValue = useDeferredValue; - exports.useEffect = useEffect4; + exports.useEffect = useEffect5; exports.useId = useId; exports.useImperativeHandle = useImperativeHandle; exports.useInsertionEffect = useInsertionEffect; @@ -2437,7 +2437,7 @@ var require_react_dom_development = __commonJS({ var HostPortal = 4; var HostComponent = 5; var HostText = 6; - var Fragment = 7; + var Fragment2 = 7; var Mode = 8; var ContextConsumer = 9; var ContextProvider = 10; @@ -3593,7 +3593,7 @@ var require_react_dom_development = __commonJS({ return "DehydratedFragment"; case ForwardRef: return getWrappedName$1(type, type.render, "ForwardRef"); - case Fragment: + case Fragment2: return "Fragment"; case HostComponent: return type; @@ -13264,7 +13264,7 @@ var require_react_dom_development = __commonJS({ } } function updateFragment2(returnFiber, current2, fragment, lanes, key) { - if (current2 === null || current2.tag !== Fragment) { + if (current2 === null || current2.tag !== Fragment2) { var created = createFiberFromFragment(fragment, returnFiber.mode, lanes, key); created.return = returnFiber; return created; @@ -13667,7 +13667,7 @@ var require_react_dom_development = __commonJS({ if (child.key === key) { var elementType = element.type; if (elementType === REACT_FRAGMENT_TYPE) { - if (child.tag === Fragment) { + if (child.tag === Fragment2) { deleteRemainingChildren(returnFiber, child.sibling); var existing = useFiber(child, element.props.children); existing.return = returnFiber; @@ -17842,7 +17842,7 @@ var require_react_dom_development = __commonJS({ var _resolvedProps2 = workInProgress2.elementType === type ? _unresolvedProps2 : resolveDefaultProps(type, _unresolvedProps2); return updateForwardRef(current2, workInProgress2, type, _resolvedProps2, renderLanes2); } - case Fragment: + case Fragment2: return updateFragment(current2, workInProgress2, renderLanes2); case Mode: return updateMode(current2, workInProgress2, renderLanes2); @@ -18115,7 +18115,7 @@ var require_react_dom_development = __commonJS({ case SimpleMemoComponent: case FunctionComponent: case ForwardRef: - case Fragment: + case Fragment2: case Mode: case Profiler: case ContextConsumer: @@ -22374,7 +22374,7 @@ var require_react_dom_development = __commonJS({ return fiber; } function createFiberFromFragment(elements, mode, lanes, key) { - var fiber = createFiber(Fragment, elements, key, mode); + var fiber = createFiber(Fragment2, elements, key, mode); fiber.lanes = lanes; return fiber; } @@ -24416,6 +24416,693 @@ function Header() { } var Header_default = Header; +// src/Picker.tsx +var import_react2 = __toESM(require_react(), 1); + +// node_modules/jotai/esm/vanilla.mjs +var keyCount = 0; +function atom(read, write) { + const key = `atom${++keyCount}`; + const config = { + toString: () => key + }; + if (typeof read === "function") { + config.read = read; + } else { + config.init = read; + config.read = (get) => get(config); + config.write = (get, set2, arg) => set2( + config, + typeof arg === "function" ? arg(get(config)) : arg + ); + } + if (write) { + config.write = write; + } + return config; +} +var hasInitialValue = (atom2) => "init" in atom2; +var isActuallyWritableAtom = (atom2) => !!atom2.write; +var cancelPromiseMap = /* @__PURE__ */ new WeakMap(); +var registerCancelPromise = (promise, cancel) => { + cancelPromiseMap.set(promise, cancel); + promise.catch(() => { + }).finally(() => cancelPromiseMap.delete(promise)); +}; +var cancelPromise = (promise, next) => { + const cancel = cancelPromiseMap.get(promise); + if (cancel) { + cancelPromiseMap.delete(promise); + cancel(next); + } +}; +var resolvePromise = (promise, value) => { + promise.status = "fulfilled"; + promise.value = value; +}; +var rejectPromise = (promise, e) => { + promise.status = "rejected"; + promise.reason = e; +}; +var isPromiseLike = (x) => typeof (x == null ? void 0 : x.then) === "function"; +var isEqualAtomValue = (a, b) => "v" in a && "v" in b && Object.is(a.v, b.v); +var isEqualAtomError = (a, b) => "e" in a && "e" in b && Object.is(a.e, b.e); +var hasPromiseAtomValue = (a) => "v" in a && a.v instanceof Promise; +var isEqualPromiseAtomValue = (a, b) => "v" in a && "v" in b && a.v.orig && a.v.orig === b.v.orig; +var returnAtomValue = (atomState) => { + if ("e" in atomState) { + throw atomState.e; + } + return atomState.v; +}; +var createStore = () => { + const atomStateMap = /* @__PURE__ */ new WeakMap(); + const mountedMap = /* @__PURE__ */ new WeakMap(); + const pendingMap = /* @__PURE__ */ new Map(); + let storeListenersRev1; + let storeListenersRev2; + let mountedAtoms; + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + storeListenersRev1 = /* @__PURE__ */ new Set(); + storeListenersRev2 = /* @__PURE__ */ new Set(); + mountedAtoms = /* @__PURE__ */ new Set(); + } + const getAtomState = (atom2) => atomStateMap.get(atom2); + const setAtomState = (atom2, atomState) => { + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + Object.freeze(atomState); + } + const prevAtomState = atomStateMap.get(atom2); + atomStateMap.set(atom2, atomState); + if (!pendingMap.has(atom2)) { + pendingMap.set(atom2, prevAtomState); + } + if (prevAtomState && hasPromiseAtomValue(prevAtomState)) { + const next = "v" in atomState ? atomState.v instanceof Promise ? atomState.v : Promise.resolve(atomState.v) : Promise.reject(atomState.e); + cancelPromise(prevAtomState.v, next); + } + }; + const updateDependencies = (atom2, nextAtomState, nextDependencies) => { + const dependencies = /* @__PURE__ */ new Map(); + let changed = false; + nextDependencies.forEach((aState, a) => { + if (!aState && a === atom2) { + aState = nextAtomState; + } + if (aState) { + dependencies.set(a, aState); + if (nextAtomState.d.get(a) !== aState) { + changed = true; + } + } else if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + console.warn("[Bug] atom state not found"); + } + }); + if (changed || nextAtomState.d.size !== dependencies.size) { + nextAtomState.d = dependencies; + } + }; + const setAtomValue = (atom2, value, nextDependencies) => { + const prevAtomState = getAtomState(atom2); + const nextAtomState = { + d: (prevAtomState == null ? void 0 : prevAtomState.d) || /* @__PURE__ */ new Map(), + v: value + }; + if (nextDependencies) { + updateDependencies(atom2, nextAtomState, nextDependencies); + } + if (prevAtomState && isEqualAtomValue(prevAtomState, nextAtomState) && prevAtomState.d === nextAtomState.d) { + return prevAtomState; + } + if (prevAtomState && hasPromiseAtomValue(prevAtomState) && hasPromiseAtomValue(nextAtomState) && isEqualPromiseAtomValue(prevAtomState, nextAtomState)) { + if (prevAtomState.d === nextAtomState.d) { + return prevAtomState; + } else { + nextAtomState.v = prevAtomState.v; + } + } + setAtomState(atom2, nextAtomState); + return nextAtomState; + }; + const setAtomValueOrPromise = (atom2, valueOrPromise, nextDependencies, abortPromise) => { + if (isPromiseLike(valueOrPromise)) { + let continuePromise; + const promise = new Promise((resolve2, reject) => { + let settled = false; + valueOrPromise.then( + (v) => { + if (!settled) { + settled = true; + const prevAtomState = getAtomState(atom2); + const nextAtomState = setAtomValue( + atom2, + promise, + nextDependencies + ); + resolvePromise(promise, v); + resolve2(v); + if ((prevAtomState == null ? void 0 : prevAtomState.d) !== nextAtomState.d) { + mountDependencies(atom2, nextAtomState, prevAtomState == null ? void 0 : prevAtomState.d); + } + } + }, + (e) => { + if (!settled) { + settled = true; + const prevAtomState = getAtomState(atom2); + const nextAtomState = setAtomValue( + atom2, + promise, + nextDependencies + ); + rejectPromise(promise, e); + reject(e); + if ((prevAtomState == null ? void 0 : prevAtomState.d) !== nextAtomState.d) { + mountDependencies(atom2, nextAtomState, prevAtomState == null ? void 0 : prevAtomState.d); + } + } + } + ); + continuePromise = (next) => { + if (!settled) { + settled = true; + next.then( + (v) => resolvePromise(promise, v), + (e) => rejectPromise(promise, e) + ); + resolve2(next); + } + }; + }); + promise.orig = valueOrPromise; + promise.status = "pending"; + registerCancelPromise(promise, (next) => { + if (next) { + continuePromise(next); + } + abortPromise == null ? void 0 : abortPromise(); + }); + return setAtomValue(atom2, promise, nextDependencies); + } + return setAtomValue(atom2, valueOrPromise, nextDependencies); + }; + const setAtomError = (atom2, error, nextDependencies) => { + const prevAtomState = getAtomState(atom2); + const nextAtomState = { + d: (prevAtomState == null ? void 0 : prevAtomState.d) || /* @__PURE__ */ new Map(), + e: error + }; + if (nextDependencies) { + updateDependencies(atom2, nextAtomState, nextDependencies); + } + if (prevAtomState && isEqualAtomError(prevAtomState, nextAtomState) && prevAtomState.d === nextAtomState.d) { + return prevAtomState; + } + setAtomState(atom2, nextAtomState); + return nextAtomState; + }; + const readAtomState = (atom2) => { + const atomState = getAtomState(atom2); + if (atomState) { + atomState.d.forEach((_, a) => { + if (a !== atom2 && !mountedMap.has(a)) { + readAtomState(a); + } + }); + if (Array.from(atomState.d).every( + ([a, s]) => a === atom2 || getAtomState(a) === s + )) { + return atomState; + } + } + const nextDependencies = /* @__PURE__ */ new Map(); + let isSync = true; + const getter = (a) => { + if (a === atom2) { + const aState2 = getAtomState(a); + if (aState2) { + nextDependencies.set(a, aState2); + return returnAtomValue(aState2); + } + if (hasInitialValue(a)) { + nextDependencies.set(a, void 0); + return a.init; + } + throw new Error("no atom init"); + } + const aState = readAtomState(a); + nextDependencies.set(a, aState); + return returnAtomValue(aState); + }; + let controller; + let setSelf; + const options = { + get signal() { + if (!controller) { + controller = new AbortController(); + } + return controller.signal; + }, + get setSelf() { + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production" && !isActuallyWritableAtom(atom2)) { + console.warn("setSelf function cannot be used with read-only atom"); + } + if (!setSelf && isActuallyWritableAtom(atom2)) { + setSelf = (...args) => { + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production" && isSync) { + console.warn("setSelf function cannot be called in sync"); + } + if (!isSync) { + return writeAtom(atom2, ...args); + } + }; + } + return setSelf; + } + }; + try { + const valueOrPromise = atom2.read(getter, options); + return setAtomValueOrPromise( + atom2, + valueOrPromise, + nextDependencies, + () => controller == null ? void 0 : controller.abort() + ); + } catch (error) { + return setAtomError(atom2, error, nextDependencies); + } finally { + isSync = false; + } + }; + const readAtom = (atom2) => returnAtomValue(readAtomState(atom2)); + const addAtom = (atom2) => { + let mounted = mountedMap.get(atom2); + if (!mounted) { + mounted = mountAtom(atom2); + } + return mounted; + }; + const canUnmountAtom = (atom2, mounted) => !mounted.l.size && (!mounted.t.size || mounted.t.size === 1 && mounted.t.has(atom2)); + const delAtom = (atom2) => { + const mounted = mountedMap.get(atom2); + if (mounted && canUnmountAtom(atom2, mounted)) { + unmountAtom(atom2); + } + }; + const recomputeDependents = (atom2) => { + const dependencyMap = /* @__PURE__ */ new Map(); + const dirtyMap = /* @__PURE__ */ new WeakMap(); + const loop1 = (a) => { + const mounted = mountedMap.get(a); + mounted == null ? void 0 : mounted.t.forEach((dependent) => { + if (dependent !== a) { + dependencyMap.set( + dependent, + (dependencyMap.get(dependent) || /* @__PURE__ */ new Set()).add(a) + ); + dirtyMap.set(dependent, (dirtyMap.get(dependent) || 0) + 1); + loop1(dependent); + } + }); + }; + loop1(atom2); + const loop2 = (a) => { + const mounted = mountedMap.get(a); + mounted == null ? void 0 : mounted.t.forEach((dependent) => { + var _a; + if (dependent !== a) { + let dirtyCount = dirtyMap.get(dependent); + if (dirtyCount) { + dirtyMap.set(dependent, --dirtyCount); + } + if (!dirtyCount) { + let isChanged = !!((_a = dependencyMap.get(dependent)) == null ? void 0 : _a.size); + if (isChanged) { + const prevAtomState = getAtomState(dependent); + const nextAtomState = readAtomState(dependent); + isChanged = !prevAtomState || !isEqualAtomValue(prevAtomState, nextAtomState); + } + if (!isChanged) { + dependencyMap.forEach((s) => s.delete(dependent)); + } + } + loop2(dependent); + } + }); + }; + loop2(atom2); + }; + const writeAtomState = (atom2, ...args) => { + let isSync = true; + const getter = (a) => returnAtomValue(readAtomState(a)); + const setter = (a, ...args2) => { + let r; + if (a === atom2) { + if (!hasInitialValue(a)) { + throw new Error("atom not writable"); + } + const prevAtomState = getAtomState(a); + const nextAtomState = setAtomValueOrPromise(a, args2[0]); + if (!prevAtomState || !isEqualAtomValue(prevAtomState, nextAtomState)) { + recomputeDependents(a); + } + } else { + r = writeAtomState(a, ...args2); + } + if (!isSync) { + const flushed = flushPending(); + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + storeListenersRev2.forEach( + (l) => l({ type: "async-write", flushed }) + ); + } + } + return r; + }; + const result = atom2.write(getter, setter, ...args); + isSync = false; + return result; + }; + const writeAtom = (atom2, ...args) => { + const result = writeAtomState(atom2, ...args); + const flushed = flushPending(); + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + storeListenersRev2.forEach( + (l) => l({ type: "write", flushed }) + ); + } + return result; + }; + const mountAtom = (atom2, initialDependent) => { + const mounted = { + t: new Set(initialDependent && [initialDependent]), + l: /* @__PURE__ */ new Set() + }; + mountedMap.set(atom2, mounted); + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + mountedAtoms.add(atom2); + } + readAtomState(atom2).d.forEach((_, a) => { + const aMounted = mountedMap.get(a); + if (aMounted) { + aMounted.t.add(atom2); + } else { + if (a !== atom2) { + mountAtom(a, atom2); + } + } + }); + readAtomState(atom2); + if (isActuallyWritableAtom(atom2) && atom2.onMount) { + const onUnmount = atom2.onMount((...args) => writeAtom(atom2, ...args)); + if (onUnmount) { + mounted.u = onUnmount; + } + } + return mounted; + }; + const unmountAtom = (atom2) => { + var _a; + const onUnmount = (_a = mountedMap.get(atom2)) == null ? void 0 : _a.u; + if (onUnmount) { + onUnmount(); + } + mountedMap.delete(atom2); + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + mountedAtoms.delete(atom2); + } + const atomState = getAtomState(atom2); + if (atomState) { + if (hasPromiseAtomValue(atomState)) { + cancelPromise(atomState.v); + } + atomState.d.forEach((_, a) => { + if (a !== atom2) { + const mounted = mountedMap.get(a); + if (mounted) { + mounted.t.delete(atom2); + if (canUnmountAtom(a, mounted)) { + unmountAtom(a); + } + } + } + }); + } else if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + console.warn("[Bug] could not find atom state to unmount", atom2); + } + }; + const mountDependencies = (atom2, atomState, prevDependencies) => { + const depSet = new Set(atomState.d.keys()); + prevDependencies == null ? void 0 : prevDependencies.forEach((_, a) => { + if (depSet.has(a)) { + depSet.delete(a); + return; + } + const mounted = mountedMap.get(a); + if (mounted) { + mounted.t.delete(atom2); + if (canUnmountAtom(a, mounted)) { + unmountAtom(a); + } + } + }); + depSet.forEach((a) => { + const mounted = mountedMap.get(a); + if (mounted) { + mounted.t.add(atom2); + } else if (mountedMap.has(atom2)) { + mountAtom(a, atom2); + } + }); + }; + const flushPending = () => { + let flushed; + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + flushed = /* @__PURE__ */ new Set(); + } + while (pendingMap.size) { + const pending = Array.from(pendingMap); + pendingMap.clear(); + pending.forEach(([atom2, prevAtomState]) => { + const atomState = getAtomState(atom2); + if (atomState) { + if (atomState.d !== (prevAtomState == null ? void 0 : prevAtomState.d)) { + mountDependencies(atom2, atomState, prevAtomState == null ? void 0 : prevAtomState.d); + } + const mounted = mountedMap.get(atom2); + if (mounted && !// TODO This seems pretty hacky. Hope to fix it. + // Maybe we could `mountDependencies` in `setAtomState`? + (prevAtomState && !hasPromiseAtomValue(prevAtomState) && (isEqualAtomValue(prevAtomState, atomState) || isEqualAtomError(prevAtomState, atomState)))) { + mounted.l.forEach((listener) => listener()); + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + flushed.add(atom2); + } + } + } else if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + console.warn("[Bug] no atom state to flush"); + } + }); + } + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + storeListenersRev1.forEach((l) => l("state")); + return flushed; + } + }; + const subscribeAtom = (atom2, listener) => { + const mounted = addAtom(atom2); + const flushed = flushPending(); + const listeners = mounted.l; + listeners.add(listener); + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + storeListenersRev1.forEach((l) => l("sub")); + storeListenersRev2.forEach( + (l) => l({ type: "sub", flushed }) + ); + } + return () => { + listeners.delete(listener); + delAtom(atom2); + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + storeListenersRev1.forEach((l) => l("unsub")); + storeListenersRev2.forEach((l) => l({ type: "unsub" })); + } + }; + }; + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + return { + get: readAtom, + set: writeAtom, + sub: subscribeAtom, + // store dev methods (these are tentative and subject to change without notice) + dev_subscribe_store: (l, rev) => { + if (rev !== 2) { + console.warn( + "The current StoreListener revision is 2. The older ones are deprecated." + ); + storeListenersRev1.add(l); + return () => { + storeListenersRev1.delete(l); + }; + } + storeListenersRev2.add(l); + return () => { + storeListenersRev2.delete(l); + }; + }, + dev_get_mounted_atoms: () => mountedAtoms.values(), + dev_get_atom_state: (a) => atomStateMap.get(a), + dev_get_mounted: (a) => mountedMap.get(a), + dev_restore_atoms: (values) => { + for (const [atom2, valueOrPromise] of values) { + if (hasInitialValue(atom2)) { + setAtomValueOrPromise(atom2, valueOrPromise); + recomputeDependents(atom2); + } + } + const flushed = flushPending(); + storeListenersRev2.forEach( + (l) => l({ type: "restore", flushed }) + ); + } + }; + } + return { + get: readAtom, + set: writeAtom, + sub: subscribeAtom + }; +}; +var defaultStore; +var getDefaultStore = () => { + if (!defaultStore) { + defaultStore = createStore(); + } + return defaultStore; +}; + +// node_modules/jotai/esm/react.mjs +var import_react = __toESM(require_react(), 1); +var StoreContext = (0, import_react.createContext)(void 0); +var useStore = (options) => { + const store = (0, import_react.useContext)(StoreContext); + return (options == null ? void 0 : options.store) || store || getDefaultStore(); +}; +var isPromiseLike2 = (x) => typeof (x == null ? void 0 : x.then) === "function"; +var use = import_react.default.use || ((promise) => { + if (promise.status === "pending") { + throw promise; + } else if (promise.status === "fulfilled") { + return promise.value; + } else if (promise.status === "rejected") { + throw promise.reason; + } else { + promise.status = "pending"; + promise.then( + (v) => { + promise.status = "fulfilled"; + promise.value = v; + }, + (e) => { + promise.status = "rejected"; + promise.reason = e; + } + ); + throw promise; + } +}); +function useAtomValue(atom2, options) { + const store = useStore(options); + const [[valueFromReducer, storeFromReducer, atomFromReducer], rerender] = (0, import_react.useReducer)( + (prev) => { + const nextValue = store.get(atom2); + if (Object.is(prev[0], nextValue) && prev[1] === store && prev[2] === atom2) { + return prev; + } + return [nextValue, store, atom2]; + }, + void 0, + () => [store.get(atom2), store, atom2] + ); + let value = valueFromReducer; + if (storeFromReducer !== store || atomFromReducer !== atom2) { + rerender(); + value = store.get(atom2); + } + const delay = options == null ? void 0 : options.delay; + (0, import_react.useEffect)(() => { + const unsub = store.sub(atom2, () => { + if (typeof delay === "number") { + setTimeout(rerender, delay); + return; + } + rerender(); + }); + rerender(); + return unsub; + }, [store, atom2, delay]); + (0, import_react.useDebugValue)(value); + return isPromiseLike2(value) ? use(value) : value; +} +function useSetAtom(atom2, options) { + const store = useStore(options); + const setAtom = (0, import_react.useCallback)( + (...args) => { + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production" && !("write" in atom2)) { + throw new Error("not writable atom"); + } + return store.set(atom2, ...args); + }, + [store, atom2] + ); + return setAtom; +} +function useAtom(atom2, options) { + return [ + useAtomValue(atom2, options), + // We do wrong type assertion here, which results in throwing an error. + useSetAtom(atom2, options) + ]; +} + +// src/Picker.tsx +var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1); +function Picker({ + $url, + $options = (0, import_react2.useMemo)(() => atom([]), []), + $isLoading = (0, import_react2.useMemo)(() => atom(true), []), + $isEnabled = (0, import_react2.useMemo)(() => atom(true), []), + $selectedOption = (0, import_react2.useMemo)(() => atom(""), []) +}) { + const url = useAtomValue($url); + const options = useAtomValue($options); + const isLoading = useAtomValue($isLoading); + const [selectedOption, setSelectedOption] = useAtom($selectedOption); + const isEnabled = useAtomValue($isEnabled); + const optionsFetched = useSetAtom((0, import_react2.useMemo)(() => atom(null, (get, set2, options2) => { + set2($options, options2); + set2($isLoading, false); + }), [$options, $isLoading])); + (0, import_react2.useEffect)(() => { + if (isEnabled) { + fetch(url).then((x) => x.json()).catch((err) => ["AAPL", "MSFT", "GOOG"]).then(optionsFetched); + } + }, [url, isEnabled]); + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Loading..." }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { value: selectedOption, onChange: (e) => { + setSelectedOption(e.target.value); + }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "" }, ""), + options.map( + (date) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: date, children: date }, date) + ) + ] }) }); +} + +// src/App.module.css +var App_module_default = { + "app": "App-module__app_gPMrEW__001", + "picker": "App-module__picker_gPMrEW__001" +}; + // node_modules/@kurkle/color/dist/color.esm.js function round(v) { return v + 0.5 | 0; @@ -26313,7 +27000,7 @@ function _addGrace(minmax, grace, beginAtZero) { max: keepZero(max, change) }; } -function createContext(parentContext, context) { +function createContext2(parentContext, context) { return Object.assign(Object.create(parentContext), context); } function _createResolver(scopes, prefixes = [ @@ -27272,7 +27959,7 @@ function doSplitByStyles(line, segments, points, segmentOptions) { let style; for (i = start + 1; i <= segment.end; i++) { const pt = points[i % count]; - style = readStyle(segmentOptions.setContext(createContext(chartContext, { + style = readStyle(segmentOptions.setContext(createContext2(chartContext, { type: "segment", p0: prev, p1: pt, @@ -27829,7 +28516,7 @@ function getFirstScaleId(chart, axis) { return Object.keys(scales).filter((key) => scales[key].axis === axis).shift(); } function createDatasetContext(parent, index) { - return createContext(parent, { + return createContext2(parent, { active: false, dataset: void 0, datasetIndex: index, @@ -27839,7 +28526,7 @@ function createDatasetContext(parent, index) { }); } function createDataContext(parent, index, element) { - return createContext(parent, { + return createContext2(parent, { active: false, dataIndex: index, parsed: void 0, @@ -29665,13 +30352,13 @@ function getTitleHeight(options, fallback) { return lines * font.lineHeight + padding.height; } function createScaleContext(parent, scale) { - return createContext(parent, { + return createContext2(parent, { scale, type: "scale" }); } function createTickContext(parent, index, tick) { - return createContext(parent, { + return createContext2(parent, { tick, index, type: "tick" @@ -32129,7 +32816,7 @@ var Chart = class { return meta; } getContext() { - return this.$context || (this.$context = createContext(null, { + return this.$context || (this.$context = createContext2(null, { chart: this, type: "chart" })); @@ -33557,7 +34244,7 @@ function getBeforeAfterBodyLines(callback2) { return pushOrConcat([], splitNewlines(callback2)); } function createTooltipContext(parent, tooltip, tooltipItems) { - return createContext(parent, { + return createContext2(parent, { tooltip, tooltipItems, type: "tooltip" @@ -35089,7 +35776,7 @@ function drawRadiusLine(scale, gridLineOpts, radius, labelCount, borderOpts) { ctx.restore(); } function createPointLabelContext(parent, index, label) { - return createContext(parent, { + return createContext2(parent, { label, index, type: "pointLabel" @@ -35287,1424 +35974,858 @@ var RadialLinearScale = class extends LinearScaleBase { }); ctx.restore(); } - drawTitle() { - } -}; -__publicField(RadialLinearScale, "id", "radialLinear"); -__publicField(RadialLinearScale, "defaults", { - display: true, - animate: true, - position: "chartArea", - angleLines: { - display: true, - lineWidth: 1, - borderDash: [], - borderDashOffset: 0 - }, - grid: { - circular: false - }, - startAngle: 0, - ticks: { - showLabelBackdrop: true, - callback: Ticks.formatters.numeric - }, - pointLabels: { - backdropColor: void 0, - backdropPadding: 2, - display: true, - font: { - size: 10 - }, - callback(label) { - return label; - }, - padding: 5, - centerPointLabels: false - } -}); -__publicField(RadialLinearScale, "defaultRoutes", { - "angleLines.color": "borderColor", - "pointLabels.color": "color", - "ticks.color": "color" -}); -__publicField(RadialLinearScale, "descriptors", { - angleLines: { - _fallback: "grid" - } -}); -var INTERVALS = { - millisecond: { - common: true, - size: 1, - steps: 1e3 - }, - second: { - common: true, - size: 1e3, - steps: 60 - }, - minute: { - common: true, - size: 6e4, - steps: 60 - }, - hour: { - common: true, - size: 36e5, - steps: 24 - }, - day: { - common: true, - size: 864e5, - steps: 30 - }, - week: { - common: false, - size: 6048e5, - steps: 4 - }, - month: { - common: true, - size: 2628e6, - steps: 12 - }, - quarter: { - common: false, - size: 7884e6, - steps: 4 - }, - year: { - common: true, - size: 3154e7 - } -}; -var UNITS = /* @__PURE__ */ Object.keys(INTERVALS); -function sorter(a, b) { - return a - b; -} -function parse(scale, input) { - if (isNullOrUndef(input)) { - return null; - } - const adapter = scale._adapter; - const { parser, round: round2, isoWeekday } = scale._parseOpts; - let value = input; - if (typeof parser === "function") { - value = parser(value); - } - if (!isNumberFinite(value)) { - value = typeof parser === "string" ? adapter.parse(value, parser) : adapter.parse(value); - } - if (value === null) { - return null; - } - if (round2) { - value = round2 === "week" && (isNumber(isoWeekday) || isoWeekday === true) ? adapter.startOf(value, "isoWeek", isoWeekday) : adapter.startOf(value, round2); - } - return +value; -} -function determineUnitForAutoTicks(minUnit, min, max, capacity) { - const ilen = UNITS.length; - for (let i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { - const interval = INTERVALS[UNITS[i]]; - const factor = interval.steps ? interval.steps : Number.MAX_SAFE_INTEGER; - if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) { - return UNITS[i]; - } - } - return UNITS[ilen - 1]; -} -function determineUnitForFormatting(scale, numTicks, minUnit, min, max) { - for (let i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--) { - const unit = UNITS[i]; - if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= numTicks - 1) { - return unit; - } - } - return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0]; -} -function determineMajorUnit(unit) { - for (let i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { - if (INTERVALS[UNITS[i]].common) { - return UNITS[i]; - } - } -} -function addTick(ticks, time, timestamps) { - if (!timestamps) { - ticks[time] = true; - } else if (timestamps.length) { - const { lo, hi } = _lookup(timestamps, time); - const timestamp = timestamps[lo] >= time ? timestamps[lo] : timestamps[hi]; - ticks[timestamp] = true; - } -} -function setMajorTicks(scale, ticks, map2, majorUnit) { - const adapter = scale._adapter; - const first = +adapter.startOf(ticks[0].value, majorUnit); - const last = ticks[ticks.length - 1].value; - let major, index; - for (major = first; major <= last; major = +adapter.add(major, 1, majorUnit)) { - index = map2[major]; - if (index >= 0) { - ticks[index].major = true; - } - } - return ticks; -} -function ticksFromTimestamps(scale, values, majorUnit) { - const ticks = []; - const map2 = {}; - const ilen = values.length; - let i, value; - for (i = 0; i < ilen; ++i) { - value = values[i]; - map2[value] = i; - ticks.push({ - value, - major: false - }); - } - return ilen === 0 || !majorUnit ? ticks : setMajorTicks(scale, ticks, map2, majorUnit); -} -var TimeScale = class extends Scale { - constructor(props) { - super(props); - this._cache = { - data: [], - labels: [], - all: [] - }; - this._unit = "day"; - this._majorUnit = void 0; - this._offsets = {}; - this._normalized = false; - this._parseOpts = void 0; - } - init(scaleOpts, opts = {}) { - const time = scaleOpts.time || (scaleOpts.time = {}); - const adapter = this._adapter = new adapters._date(scaleOpts.adapters.date); - adapter.init(opts); - mergeIf(time.displayFormats, adapter.formats()); - this._parseOpts = { - parser: time.parser, - round: time.round, - isoWeekday: time.isoWeekday - }; - super.init(scaleOpts); - this._normalized = opts.normalized; - } - parse(raw, index) { - if (raw === void 0) { - return null; - } - return parse(this, raw); - } - beforeLayout() { - super.beforeLayout(); - this._cache = { - data: [], - labels: [], - all: [] - }; - } - determineDataLimits() { - const options = this.options; - const adapter = this._adapter; - const unit = options.time.unit || "day"; - let { min, max, minDefined, maxDefined } = this.getUserBounds(); - function _applyBounds(bounds) { - if (!minDefined && !isNaN(bounds.min)) { - min = Math.min(min, bounds.min); - } - if (!maxDefined && !isNaN(bounds.max)) { - max = Math.max(max, bounds.max); - } - } - if (!minDefined || !maxDefined) { - _applyBounds(this._getLabelBounds()); - if (options.bounds !== "ticks" || options.ticks.source !== "labels") { - _applyBounds(this.getMinMax(false)); - } - } - min = isNumberFinite(min) && !isNaN(min) ? min : +adapter.startOf(Date.now(), unit); - max = isNumberFinite(max) && !isNaN(max) ? max : +adapter.endOf(Date.now(), unit) + 1; - this.min = Math.min(min, max - 1); - this.max = Math.max(min + 1, max); - } - _getLabelBounds() { - const arr = this.getLabelTimestamps(); - let min = Number.POSITIVE_INFINITY; - let max = Number.NEGATIVE_INFINITY; - if (arr.length) { - min = arr[0]; - max = arr[arr.length - 1]; - } - return { - min, - max - }; - } - buildTicks() { - const options = this.options; - const timeOpts = options.time; - const tickOpts = options.ticks; - const timestamps = tickOpts.source === "labels" ? this.getLabelTimestamps() : this._generate(); - if (options.bounds === "ticks" && timestamps.length) { - this.min = this._userMin || timestamps[0]; - this.max = this._userMax || timestamps[timestamps.length - 1]; - } - const min = this.min; - const max = this.max; - const ticks = _filterBetween(timestamps, min, max); - this._unit = timeOpts.unit || (tickOpts.autoSkip ? determineUnitForAutoTicks(timeOpts.minUnit, this.min, this.max, this._getLabelCapacity(min)) : determineUnitForFormatting(this, ticks.length, timeOpts.minUnit, this.min, this.max)); - this._majorUnit = !tickOpts.major.enabled || this._unit === "year" ? void 0 : determineMajorUnit(this._unit); - this.initOffsets(timestamps); - if (options.reverse) { - ticks.reverse(); - } - return ticksFromTimestamps(this, ticks, this._majorUnit); - } - afterAutoSkip() { - if (this.options.offsetAfterAutoskip) { - this.initOffsets(this.ticks.map((tick) => +tick.value)); - } - } - initOffsets(timestamps = []) { - let start = 0; - let end = 0; - let first, last; - if (this.options.offset && timestamps.length) { - first = this.getDecimalForValue(timestamps[0]); - if (timestamps.length === 1) { - start = 1 - first; - } else { - start = (this.getDecimalForValue(timestamps[1]) - first) / 2; - } - last = this.getDecimalForValue(timestamps[timestamps.length - 1]); - if (timestamps.length === 1) { - end = last; - } else { - end = (last - this.getDecimalForValue(timestamps[timestamps.length - 2])) / 2; - } - } - const limit = timestamps.length < 3 ? 0.5 : 0.25; - start = _limitValue(start, 0, limit); - end = _limitValue(end, 0, limit); - this._offsets = { - start, - end, - factor: 1 / (start + 1 + end) - }; - } - _generate() { - const adapter = this._adapter; - const min = this.min; - const max = this.max; - const options = this.options; - const timeOpts = options.time; - const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, this._getLabelCapacity(min)); - const stepSize = valueOrDefault(options.ticks.stepSize, 1); - const weekday = minor === "week" ? timeOpts.isoWeekday : false; - const hasWeekday = isNumber(weekday) || weekday === true; - const ticks = {}; - let first = min; - let time, count; - if (hasWeekday) { - first = +adapter.startOf(first, "isoWeek", weekday); - } - first = +adapter.startOf(first, hasWeekday ? "day" : minor); - if (adapter.diff(max, min, minor) > 1e5 * stepSize) { - throw new Error(min + " and " + max + " are too far apart with stepSize of " + stepSize + " " + minor); - } - const timestamps = options.ticks.source === "data" && this.getDataTimestamps(); - for (time = first, count = 0; time < max; time = +adapter.add(time, stepSize, minor), count++) { - addTick(ticks, time, timestamps); - } - if (time === max || options.bounds === "ticks" || count === 1) { - addTick(ticks, time, timestamps); - } - return Object.keys(ticks).sort((a, b) => a - b).map((x) => +x); - } - getLabelForValue(value) { - const adapter = this._adapter; - const timeOpts = this.options.time; - if (timeOpts.tooltipFormat) { - return adapter.format(value, timeOpts.tooltipFormat); - } - return adapter.format(value, timeOpts.displayFormats.datetime); - } - format(value, format) { - const options = this.options; - const formats = options.time.displayFormats; - const unit = this._unit; - const fmt = format || formats[unit]; - return this._adapter.format(value, fmt); - } - _tickFormatFunction(time, index, ticks, format) { - const options = this.options; - const formatter = options.ticks.callback; - if (formatter) { - return callback(formatter, [ - time, - index, - ticks - ], this); - } - const formats = options.time.displayFormats; - const unit = this._unit; - const majorUnit = this._majorUnit; - const minorFormat = unit && formats[unit]; - const majorFormat = majorUnit && formats[majorUnit]; - const tick = ticks[index]; - const major = majorUnit && majorFormat && tick && tick.major; - return this._adapter.format(time, format || (major ? majorFormat : minorFormat)); - } - generateTickLabels(ticks) { - let i, ilen, tick; - for (i = 0, ilen = ticks.length; i < ilen; ++i) { - tick = ticks[i]; - tick.label = this._tickFormatFunction(tick.value, i, ticks); - } - } - getDecimalForValue(value) { - return value === null ? NaN : (value - this.min) / (this.max - this.min); - } - getPixelForValue(value) { - const offsets = this._offsets; - const pos = this.getDecimalForValue(value); - return this.getPixelForDecimal((offsets.start + pos) * offsets.factor); - } - getValueForPixel(pixel) { - const offsets = this._offsets; - const pos = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end; - return this.min + pos * (this.max - this.min); - } - _getLabelSize(label) { - const ticksOpts = this.options.ticks; - const tickLabelWidth = this.ctx.measureText(label).width; - const angle = toRadians(this.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation); - const cosRotation = Math.cos(angle); - const sinRotation = Math.sin(angle); - const tickFontSize = this._resolveTickFontOptions(0).size; - return { - w: tickLabelWidth * cosRotation + tickFontSize * sinRotation, - h: tickLabelWidth * sinRotation + tickFontSize * cosRotation - }; - } - _getLabelCapacity(exampleTime) { - const timeOpts = this.options.time; - const displayFormats = timeOpts.displayFormats; - const format = displayFormats[timeOpts.unit] || displayFormats.millisecond; - const exampleLabel = this._tickFormatFunction(exampleTime, 0, ticksFromTimestamps(this, [ - exampleTime - ], this._majorUnit), format); - const size = this._getLabelSize(exampleLabel); - const capacity = Math.floor(this.isHorizontal() ? this.width / size.w : this.height / size.h) - 1; - return capacity > 0 ? capacity : 1; - } - getDataTimestamps() { - let timestamps = this._cache.data || []; - let i, ilen; - if (timestamps.length) { - return timestamps; - } - const metas = this.getMatchingVisibleMetas(); - if (this._normalized && metas.length) { - return this._cache.data = metas[0].controller.getAllParsedValues(this); - } - for (i = 0, ilen = metas.length; i < ilen; ++i) { - timestamps = timestamps.concat(metas[i].controller.getAllParsedValues(this)); - } - return this._cache.data = this.normalize(timestamps); - } - getLabelTimestamps() { - const timestamps = this._cache.labels || []; - let i, ilen; - if (timestamps.length) { - return timestamps; - } - const labels = this.getLabels(); - for (i = 0, ilen = labels.length; i < ilen; ++i) { - timestamps.push(parse(this, labels[i])); - } - return this._cache.labels = this._normalized ? timestamps : this.normalize(timestamps); - } - normalize(values) { - return _arrayUnique(values.sort(sorter)); - } + drawTitle() { + } }; -__publicField(TimeScale, "id", "time"); -__publicField(TimeScale, "defaults", { - bounds: "data", - adapters: {}, - time: { - parser: false, - unit: false, - round: false, - isoWeekday: false, - minUnit: "millisecond", - displayFormats: {} +__publicField(RadialLinearScale, "id", "radialLinear"); +__publicField(RadialLinearScale, "defaults", { + display: true, + animate: true, + position: "chartArea", + angleLines: { + display: true, + lineWidth: 1, + borderDash: [], + borderDashOffset: 0 + }, + grid: { + circular: false }, + startAngle: 0, ticks: { - source: "auto", - callback: false, - major: { - enabled: false - } + showLabelBackdrop: true, + callback: Ticks.formatters.numeric + }, + pointLabels: { + backdropColor: void 0, + backdropPadding: 2, + display: true, + font: { + size: 10 + }, + callback(label) { + return label; + }, + padding: 5, + centerPointLabels: false } }); -function interpolate2(table, val, reverse) { - let lo = 0; - let hi = table.length - 1; - let prevSource, nextSource, prevTarget, nextTarget; - if (reverse) { - if (val >= table[lo].pos && val <= table[hi].pos) { - ({ lo, hi } = _lookupByKey(table, "pos", val)); - } - ({ pos: prevSource, time: prevTarget } = table[lo]); - ({ pos: nextSource, time: nextTarget } = table[hi]); - } else { - if (val >= table[lo].time && val <= table[hi].time) { - ({ lo, hi } = _lookupByKey(table, "time", val)); - } - ({ time: prevSource, pos: prevTarget } = table[lo]); - ({ time: nextSource, pos: nextTarget } = table[hi]); - } - const span = nextSource - prevSource; - return span ? prevTarget + (nextTarget - prevTarget) * (val - prevSource) / span : prevTarget; -} -var TimeSeriesScale = class extends TimeScale { - constructor(props) { - super(props); - this._table = []; - this._minPos = void 0; - this._tableRange = void 0; +__publicField(RadialLinearScale, "defaultRoutes", { + "angleLines.color": "borderColor", + "pointLabels.color": "color", + "ticks.color": "color" +}); +__publicField(RadialLinearScale, "descriptors", { + angleLines: { + _fallback: "grid" } - initOffsets() { - const timestamps = this._getTimestampsForTable(); - const table = this._table = this.buildLookupTable(timestamps); - this._minPos = interpolate2(table, this.min); - this._tableRange = interpolate2(table, this.max) - this._minPos; - super.initOffsets(timestamps); +}); +var INTERVALS = { + millisecond: { + common: true, + size: 1, + steps: 1e3 + }, + second: { + common: true, + size: 1e3, + steps: 60 + }, + minute: { + common: true, + size: 6e4, + steps: 60 + }, + hour: { + common: true, + size: 36e5, + steps: 24 + }, + day: { + common: true, + size: 864e5, + steps: 30 + }, + week: { + common: false, + size: 6048e5, + steps: 4 + }, + month: { + common: true, + size: 2628e6, + steps: 12 + }, + quarter: { + common: false, + size: 7884e6, + steps: 4 + }, + year: { + common: true, + size: 3154e7 } - buildLookupTable(timestamps) { - const { min, max } = this; - const items = []; - const table = []; - let i, ilen, prev, curr, next; - for (i = 0, ilen = timestamps.length; i < ilen; ++i) { - curr = timestamps[i]; - if (curr >= min && curr <= max) { - items.push(curr); - } - } - if (items.length < 2) { - return [ - { - time: min, - pos: 0 - }, - { - time: max, - pos: 1 - } - ]; - } - for (i = 0, ilen = items.length; i < ilen; ++i) { - next = items[i + 1]; - prev = items[i - 1]; - curr = items[i]; - if (Math.round((next + prev) / 2) !== curr) { - table.push({ - time: curr, - pos: i / (ilen - 1) - }); - } - } - return table; +}; +var UNITS = /* @__PURE__ */ Object.keys(INTERVALS); +function sorter(a, b) { + return a - b; +} +function parse(scale, input) { + if (isNullOrUndef(input)) { + return null; } - _getTimestampsForTable() { - let timestamps = this._cache.all || []; - if (timestamps.length) { - return timestamps; - } - const data = this.getDataTimestamps(); - const label = this.getLabelTimestamps(); - if (data.length && label.length) { - timestamps = this.normalize(data.concat(label)); - } else { - timestamps = data.length ? data : label; - } - timestamps = this._cache.all = timestamps; - return timestamps; + const adapter = scale._adapter; + const { parser, round: round2, isoWeekday } = scale._parseOpts; + let value = input; + if (typeof parser === "function") { + value = parser(value); } - getDecimalForValue(value) { - return (interpolate2(this._table, value) - this._minPos) / this._tableRange; + if (!isNumberFinite(value)) { + value = typeof parser === "string" ? adapter.parse(value, parser) : adapter.parse(value); } - getValueForPixel(pixel) { - const offsets = this._offsets; - const decimal = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end; - return interpolate2(this._table, decimal * this._tableRange + this._minPos, true); + if (value === null) { + return null; } -}; -__publicField(TimeSeriesScale, "id", "timeseries"); -__publicField(TimeSeriesScale, "defaults", TimeScale.defaults); - -// node_modules/react-chartjs-2/dist/index.js -var import_react = __toESM(require_react(), 1); -var defaultDatasetIdKey = "label"; -function reforwardRef(ref, value) { - if (typeof ref === "function") { - ref(value); - } else if (ref) { - ref.current = value; + if (round2) { + value = round2 === "week" && (isNumber(isoWeekday) || isoWeekday === true) ? adapter.startOf(value, "isoWeek", isoWeekday) : adapter.startOf(value, round2); } + return +value; } -function setOptions(chart, nextOptions) { - const options = chart.options; - if (options && nextOptions) { - Object.assign(options, nextOptions); +function determineUnitForAutoTicks(minUnit, min, max, capacity) { + const ilen = UNITS.length; + for (let i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { + const interval = INTERVALS[UNITS[i]]; + const factor = interval.steps ? interval.steps : Number.MAX_SAFE_INTEGER; + if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) { + return UNITS[i]; + } } + return UNITS[ilen - 1]; } -function setLabels(currentData, nextLabels) { - currentData.labels = nextLabels; -} -function setDatasets(currentData, nextDatasets) { - let datasetIdKey = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : defaultDatasetIdKey; - const addedDatasets = []; - currentData.datasets = nextDatasets.map((nextDataset) => { - const currentDataset = currentData.datasets.find((dataset) => dataset[datasetIdKey] === nextDataset[datasetIdKey]); - if (!currentDataset || !nextDataset.data || addedDatasets.includes(currentDataset)) { - return { - ...nextDataset - }; +function determineUnitForFormatting(scale, numTicks, minUnit, min, max) { + for (let i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--) { + const unit = UNITS[i]; + if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= numTicks - 1) { + return unit; } - addedDatasets.push(currentDataset); - Object.assign(currentDataset, nextDataset); - return currentDataset; - }); -} -function cloneData(data) { - let datasetIdKey = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : defaultDatasetIdKey; - const nextData = { - labels: [], - datasets: [] - }; - setLabels(nextData, data.labels); - setDatasets(nextData, data.datasets, datasetIdKey); - return nextData; + } + return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0]; } -function ChartComponent(props, ref) { - const { height = 150, width = 300, redraw = false, datasetIdKey, type, data, options, plugins = [], fallbackContent, updateMode, ...canvasProps } = props; - const canvasRef = (0, import_react.useRef)(null); - const chartRef = (0, import_react.useRef)(); - const renderChart = () => { - if (!canvasRef.current) - return; - chartRef.current = new Chart(canvasRef.current, { - type, - data: cloneData(data, datasetIdKey), - options: options && { - ...options - }, - plugins - }); - reforwardRef(ref, chartRef.current); - }; - const destroyChart = () => { - reforwardRef(ref, null); - if (chartRef.current) { - chartRef.current.destroy(); - chartRef.current = null; - } - }; - (0, import_react.useEffect)(() => { - if (!redraw && chartRef.current && options) { - setOptions(chartRef.current, options); - } - }, [ - redraw, - options - ]); - (0, import_react.useEffect)(() => { - if (!redraw && chartRef.current) { - setLabels(chartRef.current.config.data, data.labels); - } - }, [ - redraw, - data.labels - ]); - (0, import_react.useEffect)(() => { - if (!redraw && chartRef.current && data.datasets) { - setDatasets(chartRef.current.config.data, data.datasets, datasetIdKey); - } - }, [ - redraw, - data.datasets - ]); - (0, import_react.useEffect)(() => { - if (!chartRef.current) - return; - if (redraw) { - destroyChart(); - setTimeout(renderChart); - } else { - chartRef.current.update(updateMode); +function determineMajorUnit(unit) { + for (let i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { + if (INTERVALS[UNITS[i]].common) { + return UNITS[i]; } - }, [ - redraw, - options, - data.labels, - data.datasets, - updateMode - ]); - (0, import_react.useEffect)(() => { - if (!chartRef.current) - return; - destroyChart(); - setTimeout(renderChart); - }, [ - type - ]); - (0, import_react.useEffect)(() => { - renderChart(); - return () => destroyChart(); - }, []); - return /* @__PURE__ */ import_react.default.createElement("canvas", Object.assign({ - ref: canvasRef, - role: "img", - height, - width - }, canvasProps), fallbackContent); + } } -var Chart2 = /* @__PURE__ */ (0, import_react.forwardRef)(ChartComponent); -function createTypedChart(type, registerables) { - Chart.register(registerables); - return /* @__PURE__ */ (0, import_react.forwardRef)((props, ref) => /* @__PURE__ */ import_react.default.createElement(Chart2, Object.assign({}, props, { - ref, - type - }))); +function addTick(ticks, time, timestamps) { + if (!timestamps) { + ticks[time] = true; + } else if (timestamps.length) { + const { lo, hi } = _lookup(timestamps, time); + const timestamp = timestamps[lo] >= time ? timestamps[lo] : timestamps[hi]; + ticks[timestamp] = true; + } } -var Line = /* @__PURE__ */ createTypedChart("line", LineController); - -// src/HistoricalImpliedVolatilityChart.tsx -var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1); -Chart.register(LineElement, plugin_tooltip, plugin_legend, CategoryScale, LinearScale, PointElement); -function HistoricalImpliedVolatilityChart() { - return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - Line, - { - datasetIdKey: "id", - data: { - labels: ["Jun", "Jul", "Aug"], - datasets: [ - { - //@ts-ignore - id: 1, - label: "", - data: [5, 6, 7] - }, - { - //@ts-ignore - id: 2, - label: "", - data: [3, 2, 1] - } - ] - } +function setMajorTicks(scale, ticks, map2, majorUnit) { + const adapter = scale._adapter; + const first = +adapter.startOf(ticks[0].value, majorUnit); + const last = ticks[ticks.length - 1].value; + let major, index; + for (major = first; major <= last; major = +adapter.add(major, 1, majorUnit)) { + index = map2[major]; + if (index >= 0) { + ticks[index].major = true; } - ) }); -} - -// src/Picker.tsx -var import_react3 = __toESM(require_react(), 1); - -// node_modules/jotai/esm/vanilla.mjs -var keyCount = 0; -function atom(read, write) { - const key = `atom${++keyCount}`; - const config = { - toString: () => key - }; - if (typeof read === "function") { - config.read = read; - } else { - config.init = read; - config.read = (get) => get(config); - config.write = (get, set2, arg) => set2( - config, - typeof arg === "function" ? arg(get(config)) : arg - ); } - if (write) { - config.write = write; + return ticks; +} +function ticksFromTimestamps(scale, values, majorUnit) { + const ticks = []; + const map2 = {}; + const ilen = values.length; + let i, value; + for (i = 0; i < ilen; ++i) { + value = values[i]; + map2[value] = i; + ticks.push({ + value, + major: false + }); } - return config; + return ilen === 0 || !majorUnit ? ticks : setMajorTicks(scale, ticks, map2, majorUnit); } -var hasInitialValue = (atom2) => "init" in atom2; -var isActuallyWritableAtom = (atom2) => !!atom2.write; -var cancelPromiseMap = /* @__PURE__ */ new WeakMap(); -var registerCancelPromise = (promise, cancel) => { - cancelPromiseMap.set(promise, cancel); - promise.catch(() => { - }).finally(() => cancelPromiseMap.delete(promise)); -}; -var cancelPromise = (promise, next) => { - const cancel = cancelPromiseMap.get(promise); - if (cancel) { - cancelPromiseMap.delete(promise); - cancel(next); +var TimeScale = class extends Scale { + constructor(props) { + super(props); + this._cache = { + data: [], + labels: [], + all: [] + }; + this._unit = "day"; + this._majorUnit = void 0; + this._offsets = {}; + this._normalized = false; + this._parseOpts = void 0; } -}; -var resolvePromise = (promise, value) => { - promise.status = "fulfilled"; - promise.value = value; -}; -var rejectPromise = (promise, e) => { - promise.status = "rejected"; - promise.reason = e; -}; -var isPromiseLike = (x) => typeof (x == null ? void 0 : x.then) === "function"; -var isEqualAtomValue = (a, b) => "v" in a && "v" in b && Object.is(a.v, b.v); -var isEqualAtomError = (a, b) => "e" in a && "e" in b && Object.is(a.e, b.e); -var hasPromiseAtomValue = (a) => "v" in a && a.v instanceof Promise; -var isEqualPromiseAtomValue = (a, b) => "v" in a && "v" in b && a.v.orig && a.v.orig === b.v.orig; -var returnAtomValue = (atomState) => { - if ("e" in atomState) { - throw atomState.e; + init(scaleOpts, opts = {}) { + const time = scaleOpts.time || (scaleOpts.time = {}); + const adapter = this._adapter = new adapters._date(scaleOpts.adapters.date); + adapter.init(opts); + mergeIf(time.displayFormats, adapter.formats()); + this._parseOpts = { + parser: time.parser, + round: time.round, + isoWeekday: time.isoWeekday + }; + super.init(scaleOpts); + this._normalized = opts.normalized; } - return atomState.v; -}; -var createStore = () => { - const atomStateMap = /* @__PURE__ */ new WeakMap(); - const mountedMap = /* @__PURE__ */ new WeakMap(); - const pendingMap = /* @__PURE__ */ new Map(); - let storeListenersRev1; - let storeListenersRev2; - let mountedAtoms; - if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { - storeListenersRev1 = /* @__PURE__ */ new Set(); - storeListenersRev2 = /* @__PURE__ */ new Set(); - mountedAtoms = /* @__PURE__ */ new Set(); + parse(raw, index) { + if (raw === void 0) { + return null; + } + return parse(this, raw); + } + beforeLayout() { + super.beforeLayout(); + this._cache = { + data: [], + labels: [], + all: [] + }; + } + determineDataLimits() { + const options = this.options; + const adapter = this._adapter; + const unit = options.time.unit || "day"; + let { min, max, minDefined, maxDefined } = this.getUserBounds(); + function _applyBounds(bounds) { + if (!minDefined && !isNaN(bounds.min)) { + min = Math.min(min, bounds.min); + } + if (!maxDefined && !isNaN(bounds.max)) { + max = Math.max(max, bounds.max); + } + } + if (!minDefined || !maxDefined) { + _applyBounds(this._getLabelBounds()); + if (options.bounds !== "ticks" || options.ticks.source !== "labels") { + _applyBounds(this.getMinMax(false)); + } + } + min = isNumberFinite(min) && !isNaN(min) ? min : +adapter.startOf(Date.now(), unit); + max = isNumberFinite(max) && !isNaN(max) ? max : +adapter.endOf(Date.now(), unit) + 1; + this.min = Math.min(min, max - 1); + this.max = Math.max(min + 1, max); } - const getAtomState = (atom2) => atomStateMap.get(atom2); - const setAtomState = (atom2, atomState) => { - if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { - Object.freeze(atomState); + _getLabelBounds() { + const arr = this.getLabelTimestamps(); + let min = Number.POSITIVE_INFINITY; + let max = Number.NEGATIVE_INFINITY; + if (arr.length) { + min = arr[0]; + max = arr[arr.length - 1]; } - const prevAtomState = atomStateMap.get(atom2); - atomStateMap.set(atom2, atomState); - if (!pendingMap.has(atom2)) { - pendingMap.set(atom2, prevAtomState); + return { + min, + max + }; + } + buildTicks() { + const options = this.options; + const timeOpts = options.time; + const tickOpts = options.ticks; + const timestamps = tickOpts.source === "labels" ? this.getLabelTimestamps() : this._generate(); + if (options.bounds === "ticks" && timestamps.length) { + this.min = this._userMin || timestamps[0]; + this.max = this._userMax || timestamps[timestamps.length - 1]; } - if (prevAtomState && hasPromiseAtomValue(prevAtomState)) { - const next = "v" in atomState ? atomState.v instanceof Promise ? atomState.v : Promise.resolve(atomState.v) : Promise.reject(atomState.e); - cancelPromise(prevAtomState.v, next); + const min = this.min; + const max = this.max; + const ticks = _filterBetween(timestamps, min, max); + this._unit = timeOpts.unit || (tickOpts.autoSkip ? determineUnitForAutoTicks(timeOpts.minUnit, this.min, this.max, this._getLabelCapacity(min)) : determineUnitForFormatting(this, ticks.length, timeOpts.minUnit, this.min, this.max)); + this._majorUnit = !tickOpts.major.enabled || this._unit === "year" ? void 0 : determineMajorUnit(this._unit); + this.initOffsets(timestamps); + if (options.reverse) { + ticks.reverse(); } - }; - const updateDependencies = (atom2, nextAtomState, nextDependencies) => { - const dependencies = /* @__PURE__ */ new Map(); - let changed = false; - nextDependencies.forEach((aState, a) => { - if (!aState && a === atom2) { - aState = nextAtomState; + return ticksFromTimestamps(this, ticks, this._majorUnit); + } + afterAutoSkip() { + if (this.options.offsetAfterAutoskip) { + this.initOffsets(this.ticks.map((tick) => +tick.value)); + } + } + initOffsets(timestamps = []) { + let start = 0; + let end = 0; + let first, last; + if (this.options.offset && timestamps.length) { + first = this.getDecimalForValue(timestamps[0]); + if (timestamps.length === 1) { + start = 1 - first; + } else { + start = (this.getDecimalForValue(timestamps[1]) - first) / 2; } - if (aState) { - dependencies.set(a, aState); - if (nextAtomState.d.get(a) !== aState) { - changed = true; - } - } else if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { - console.warn("[Bug] atom state not found"); + last = this.getDecimalForValue(timestamps[timestamps.length - 1]); + if (timestamps.length === 1) { + end = last; + } else { + end = (last - this.getDecimalForValue(timestamps[timestamps.length - 2])) / 2; } - }); - if (changed || nextAtomState.d.size !== dependencies.size) { - nextAtomState.d = dependencies; } - }; - const setAtomValue = (atom2, value, nextDependencies) => { - const prevAtomState = getAtomState(atom2); - const nextAtomState = { - d: (prevAtomState == null ? void 0 : prevAtomState.d) || /* @__PURE__ */ new Map(), - v: value + const limit = timestamps.length < 3 ? 0.5 : 0.25; + start = _limitValue(start, 0, limit); + end = _limitValue(end, 0, limit); + this._offsets = { + start, + end, + factor: 1 / (start + 1 + end) }; - if (nextDependencies) { - updateDependencies(atom2, nextAtomState, nextDependencies); + } + _generate() { + const adapter = this._adapter; + const min = this.min; + const max = this.max; + const options = this.options; + const timeOpts = options.time; + const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, this._getLabelCapacity(min)); + const stepSize = valueOrDefault(options.ticks.stepSize, 1); + const weekday = minor === "week" ? timeOpts.isoWeekday : false; + const hasWeekday = isNumber(weekday) || weekday === true; + const ticks = {}; + let first = min; + let time, count; + if (hasWeekday) { + first = +adapter.startOf(first, "isoWeek", weekday); } - if (prevAtomState && isEqualAtomValue(prevAtomState, nextAtomState) && prevAtomState.d === nextAtomState.d) { - return prevAtomState; + first = +adapter.startOf(first, hasWeekday ? "day" : minor); + if (adapter.diff(max, min, minor) > 1e5 * stepSize) { + throw new Error(min + " and " + max + " are too far apart with stepSize of " + stepSize + " " + minor); } - if (prevAtomState && hasPromiseAtomValue(prevAtomState) && hasPromiseAtomValue(nextAtomState) && isEqualPromiseAtomValue(prevAtomState, nextAtomState)) { - if (prevAtomState.d === nextAtomState.d) { - return prevAtomState; - } else { - nextAtomState.v = prevAtomState.v; - } + const timestamps = options.ticks.source === "data" && this.getDataTimestamps(); + for (time = first, count = 0; time < max; time = +adapter.add(time, stepSize, minor), count++) { + addTick(ticks, time, timestamps); } - setAtomState(atom2, nextAtomState); - return nextAtomState; - }; - const setAtomValueOrPromise = (atom2, valueOrPromise, nextDependencies, abortPromise) => { - if (isPromiseLike(valueOrPromise)) { - let continuePromise; - const promise = new Promise((resolve2, reject) => { - let settled = false; - valueOrPromise.then( - (v) => { - if (!settled) { - settled = true; - const prevAtomState = getAtomState(atom2); - const nextAtomState = setAtomValue( - atom2, - promise, - nextDependencies - ); - resolvePromise(promise, v); - resolve2(v); - if ((prevAtomState == null ? void 0 : prevAtomState.d) !== nextAtomState.d) { - mountDependencies(atom2, nextAtomState, prevAtomState == null ? void 0 : prevAtomState.d); - } - } - }, - (e) => { - if (!settled) { - settled = true; - const prevAtomState = getAtomState(atom2); - const nextAtomState = setAtomValue( - atom2, - promise, - nextDependencies - ); - rejectPromise(promise, e); - reject(e); - if ((prevAtomState == null ? void 0 : prevAtomState.d) !== nextAtomState.d) { - mountDependencies(atom2, nextAtomState, prevAtomState == null ? void 0 : prevAtomState.d); - } - } - } - ); - continuePromise = (next) => { - if (!settled) { - settled = true; - next.then( - (v) => resolvePromise(promise, v), - (e) => rejectPromise(promise, e) - ); - resolve2(next); - } - }; - }); - promise.orig = valueOrPromise; - promise.status = "pending"; - registerCancelPromise(promise, (next) => { - if (next) { - continuePromise(next); - } - abortPromise == null ? void 0 : abortPromise(); - }); - return setAtomValue(atom2, promise, nextDependencies); + if (time === max || options.bounds === "ticks" || count === 1) { + addTick(ticks, time, timestamps); } - return setAtomValue(atom2, valueOrPromise, nextDependencies); - }; - const setAtomError = (atom2, error, nextDependencies) => { - const prevAtomState = getAtomState(atom2); - const nextAtomState = { - d: (prevAtomState == null ? void 0 : prevAtomState.d) || /* @__PURE__ */ new Map(), - e: error - }; - if (nextDependencies) { - updateDependencies(atom2, nextAtomState, nextDependencies); + return Object.keys(ticks).sort((a, b) => a - b).map((x) => +x); + } + getLabelForValue(value) { + const adapter = this._adapter; + const timeOpts = this.options.time; + if (timeOpts.tooltipFormat) { + return adapter.format(value, timeOpts.tooltipFormat); } - if (prevAtomState && isEqualAtomError(prevAtomState, nextAtomState) && prevAtomState.d === nextAtomState.d) { - return prevAtomState; + return adapter.format(value, timeOpts.displayFormats.datetime); + } + format(value, format) { + const options = this.options; + const formats = options.time.displayFormats; + const unit = this._unit; + const fmt = format || formats[unit]; + return this._adapter.format(value, fmt); + } + _tickFormatFunction(time, index, ticks, format) { + const options = this.options; + const formatter = options.ticks.callback; + if (formatter) { + return callback(formatter, [ + time, + index, + ticks + ], this); } - setAtomState(atom2, nextAtomState); - return nextAtomState; - }; - const readAtomState = (atom2) => { - const atomState = getAtomState(atom2); - if (atomState) { - atomState.d.forEach((_, a) => { - if (a !== atom2 && !mountedMap.has(a)) { - readAtomState(a); - } - }); - if (Array.from(atomState.d).every( - ([a, s]) => a === atom2 || getAtomState(a) === s - )) { - return atomState; - } + const formats = options.time.displayFormats; + const unit = this._unit; + const majorUnit = this._majorUnit; + const minorFormat = unit && formats[unit]; + const majorFormat = majorUnit && formats[majorUnit]; + const tick = ticks[index]; + const major = majorUnit && majorFormat && tick && tick.major; + return this._adapter.format(time, format || (major ? majorFormat : minorFormat)); + } + generateTickLabels(ticks) { + let i, ilen, tick; + for (i = 0, ilen = ticks.length; i < ilen; ++i) { + tick = ticks[i]; + tick.label = this._tickFormatFunction(tick.value, i, ticks); } - const nextDependencies = /* @__PURE__ */ new Map(); - let isSync = true; - const getter = (a) => { - if (a === atom2) { - const aState2 = getAtomState(a); - if (aState2) { - nextDependencies.set(a, aState2); - return returnAtomValue(aState2); - } - if (hasInitialValue(a)) { - nextDependencies.set(a, void 0); - return a.init; - } - throw new Error("no atom init"); - } - const aState = readAtomState(a); - nextDependencies.set(a, aState); - return returnAtomValue(aState); - }; - let controller; - let setSelf; - const options = { - get signal() { - if (!controller) { - controller = new AbortController(); - } - return controller.signal; - }, - get setSelf() { - if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production" && !isActuallyWritableAtom(atom2)) { - console.warn("setSelf function cannot be used with read-only atom"); - } - if (!setSelf && isActuallyWritableAtom(atom2)) { - setSelf = (...args) => { - if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production" && isSync) { - console.warn("setSelf function cannot be called in sync"); - } - if (!isSync) { - return writeAtom(atom2, ...args); - } - }; - } - return setSelf; - } + } + getDecimalForValue(value) { + return value === null ? NaN : (value - this.min) / (this.max - this.min); + } + getPixelForValue(value) { + const offsets = this._offsets; + const pos = this.getDecimalForValue(value); + return this.getPixelForDecimal((offsets.start + pos) * offsets.factor); + } + getValueForPixel(pixel) { + const offsets = this._offsets; + const pos = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end; + return this.min + pos * (this.max - this.min); + } + _getLabelSize(label) { + const ticksOpts = this.options.ticks; + const tickLabelWidth = this.ctx.measureText(label).width; + const angle = toRadians(this.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation); + const cosRotation = Math.cos(angle); + const sinRotation = Math.sin(angle); + const tickFontSize = this._resolveTickFontOptions(0).size; + return { + w: tickLabelWidth * cosRotation + tickFontSize * sinRotation, + h: tickLabelWidth * sinRotation + tickFontSize * cosRotation }; - try { - const valueOrPromise = atom2.read(getter, options); - return setAtomValueOrPromise( - atom2, - valueOrPromise, - nextDependencies, - () => controller == null ? void 0 : controller.abort() - ); - } catch (error) { - return setAtomError(atom2, error, nextDependencies); - } finally { - isSync = false; + } + _getLabelCapacity(exampleTime) { + const timeOpts = this.options.time; + const displayFormats = timeOpts.displayFormats; + const format = displayFormats[timeOpts.unit] || displayFormats.millisecond; + const exampleLabel = this._tickFormatFunction(exampleTime, 0, ticksFromTimestamps(this, [ + exampleTime + ], this._majorUnit), format); + const size = this._getLabelSize(exampleLabel); + const capacity = Math.floor(this.isHorizontal() ? this.width / size.w : this.height / size.h) - 1; + return capacity > 0 ? capacity : 1; + } + getDataTimestamps() { + let timestamps = this._cache.data || []; + let i, ilen; + if (timestamps.length) { + return timestamps; } - }; - const readAtom = (atom2) => returnAtomValue(readAtomState(atom2)); - const addAtom = (atom2) => { - let mounted = mountedMap.get(atom2); - if (!mounted) { - mounted = mountAtom(atom2); + const metas = this.getMatchingVisibleMetas(); + if (this._normalized && metas.length) { + return this._cache.data = metas[0].controller.getAllParsedValues(this); } - return mounted; - }; - const canUnmountAtom = (atom2, mounted) => !mounted.l.size && (!mounted.t.size || mounted.t.size === 1 && mounted.t.has(atom2)); - const delAtom = (atom2) => { - const mounted = mountedMap.get(atom2); - if (mounted && canUnmountAtom(atom2, mounted)) { - unmountAtom(atom2); + for (i = 0, ilen = metas.length; i < ilen; ++i) { + timestamps = timestamps.concat(metas[i].controller.getAllParsedValues(this)); } - }; - const recomputeDependents = (atom2) => { - const dependencyMap = /* @__PURE__ */ new Map(); - const dirtyMap = /* @__PURE__ */ new WeakMap(); - const loop1 = (a) => { - const mounted = mountedMap.get(a); - mounted == null ? void 0 : mounted.t.forEach((dependent) => { - if (dependent !== a) { - dependencyMap.set( - dependent, - (dependencyMap.get(dependent) || /* @__PURE__ */ new Set()).add(a) - ); - dirtyMap.set(dependent, (dirtyMap.get(dependent) || 0) + 1); - loop1(dependent); - } - }); - }; - loop1(atom2); - const loop2 = (a) => { - const mounted = mountedMap.get(a); - mounted == null ? void 0 : mounted.t.forEach((dependent) => { - var _a; - if (dependent !== a) { - let dirtyCount = dirtyMap.get(dependent); - if (dirtyCount) { - dirtyMap.set(dependent, --dirtyCount); - } - if (!dirtyCount) { - let isChanged = !!((_a = dependencyMap.get(dependent)) == null ? void 0 : _a.size); - if (isChanged) { - const prevAtomState = getAtomState(dependent); - const nextAtomState = readAtomState(dependent); - isChanged = !prevAtomState || !isEqualAtomValue(prevAtomState, nextAtomState); - } - if (!isChanged) { - dependencyMap.forEach((s) => s.delete(dependent)); - } - } - loop2(dependent); - } - }); - }; - loop2(atom2); - }; - const writeAtomState = (atom2, ...args) => { - let isSync = true; - const getter = (a) => returnAtomValue(readAtomState(a)); - const setter = (a, ...args2) => { - let r; - if (a === atom2) { - if (!hasInitialValue(a)) { - throw new Error("atom not writable"); - } - const prevAtomState = getAtomState(a); - const nextAtomState = setAtomValueOrPromise(a, args2[0]); - if (!prevAtomState || !isEqualAtomValue(prevAtomState, nextAtomState)) { - recomputeDependents(a); - } - } else { - r = writeAtomState(a, ...args2); - } - if (!isSync) { - const flushed = flushPending(); - if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { - storeListenersRev2.forEach( - (l) => l({ type: "async-write", flushed }) - ); - } - } - return r; - }; - const result = atom2.write(getter, setter, ...args); - isSync = false; - return result; - }; - const writeAtom = (atom2, ...args) => { - const result = writeAtomState(atom2, ...args); - const flushed = flushPending(); - if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { - storeListenersRev2.forEach( - (l) => l({ type: "write", flushed }) - ); + return this._cache.data = this.normalize(timestamps); + } + getLabelTimestamps() { + const timestamps = this._cache.labels || []; + let i, ilen; + if (timestamps.length) { + return timestamps; + } + const labels = this.getLabels(); + for (i = 0, ilen = labels.length; i < ilen; ++i) { + timestamps.push(parse(this, labels[i])); + } + return this._cache.labels = this._normalized ? timestamps : this.normalize(timestamps); + } + normalize(values) { + return _arrayUnique(values.sort(sorter)); + } +}; +__publicField(TimeScale, "id", "time"); +__publicField(TimeScale, "defaults", { + bounds: "data", + adapters: {}, + time: { + parser: false, + unit: false, + round: false, + isoWeekday: false, + minUnit: "millisecond", + displayFormats: {} + }, + ticks: { + source: "auto", + callback: false, + major: { + enabled: false + } + } +}); +function interpolate2(table, val, reverse) { + let lo = 0; + let hi = table.length - 1; + let prevSource, nextSource, prevTarget, nextTarget; + if (reverse) { + if (val >= table[lo].pos && val <= table[hi].pos) { + ({ lo, hi } = _lookupByKey(table, "pos", val)); + } + ({ pos: prevSource, time: prevTarget } = table[lo]); + ({ pos: nextSource, time: nextTarget } = table[hi]); + } else { + if (val >= table[lo].time && val <= table[hi].time) { + ({ lo, hi } = _lookupByKey(table, "time", val)); } - return result; - }; - const mountAtom = (atom2, initialDependent) => { - const mounted = { - t: new Set(initialDependent && [initialDependent]), - l: /* @__PURE__ */ new Set() - }; - mountedMap.set(atom2, mounted); - if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { - mountedAtoms.add(atom2); + ({ time: prevSource, pos: prevTarget } = table[lo]); + ({ time: nextSource, pos: nextTarget } = table[hi]); + } + const span = nextSource - prevSource; + return span ? prevTarget + (nextTarget - prevTarget) * (val - prevSource) / span : prevTarget; +} +var TimeSeriesScale = class extends TimeScale { + constructor(props) { + super(props); + this._table = []; + this._minPos = void 0; + this._tableRange = void 0; + } + initOffsets() { + const timestamps = this._getTimestampsForTable(); + const table = this._table = this.buildLookupTable(timestamps); + this._minPos = interpolate2(table, this.min); + this._tableRange = interpolate2(table, this.max) - this._minPos; + super.initOffsets(timestamps); + } + buildLookupTable(timestamps) { + const { min, max } = this; + const items = []; + const table = []; + let i, ilen, prev, curr, next; + for (i = 0, ilen = timestamps.length; i < ilen; ++i) { + curr = timestamps[i]; + if (curr >= min && curr <= max) { + items.push(curr); + } } - readAtomState(atom2).d.forEach((_, a) => { - const aMounted = mountedMap.get(a); - if (aMounted) { - aMounted.t.add(atom2); - } else { - if (a !== atom2) { - mountAtom(a, atom2); + if (items.length < 2) { + return [ + { + time: min, + pos: 0 + }, + { + time: max, + pos: 1 } - } - }); - readAtomState(atom2); - if (isActuallyWritableAtom(atom2) && atom2.onMount) { - const onUnmount = atom2.onMount((...args) => writeAtom(atom2, ...args)); - if (onUnmount) { - mounted.u = onUnmount; + ]; + } + for (i = 0, ilen = items.length; i < ilen; ++i) { + next = items[i + 1]; + prev = items[i - 1]; + curr = items[i]; + if (Math.round((next + prev) / 2) !== curr) { + table.push({ + time: curr, + pos: i / (ilen - 1) + }); } } - return mounted; - }; - const unmountAtom = (atom2) => { - var _a; - const onUnmount = (_a = mountedMap.get(atom2)) == null ? void 0 : _a.u; - if (onUnmount) { - onUnmount(); + return table; + } + _getTimestampsForTable() { + let timestamps = this._cache.all || []; + if (timestamps.length) { + return timestamps; } - mountedMap.delete(atom2); - if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { - mountedAtoms.delete(atom2); + const data = this.getDataTimestamps(); + const label = this.getLabelTimestamps(); + if (data.length && label.length) { + timestamps = this.normalize(data.concat(label)); + } else { + timestamps = data.length ? data : label; } - const atomState = getAtomState(atom2); - if (atomState) { - if (hasPromiseAtomValue(atomState)) { - cancelPromise(atomState.v); - } - atomState.d.forEach((_, a) => { - if (a !== atom2) { - const mounted = mountedMap.get(a); - if (mounted) { - mounted.t.delete(atom2); - if (canUnmountAtom(a, mounted)) { - unmountAtom(a); - } - } - } - }); - } else if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { - console.warn("[Bug] could not find atom state to unmount", atom2); + timestamps = this._cache.all = timestamps; + return timestamps; + } + getDecimalForValue(value) { + return (interpolate2(this._table, value) - this._minPos) / this._tableRange; + } + getValueForPixel(pixel) { + const offsets = this._offsets; + const decimal = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end; + return interpolate2(this._table, decimal * this._tableRange + this._minPos, true); + } +}; +__publicField(TimeSeriesScale, "id", "timeseries"); +__publicField(TimeSeriesScale, "defaults", TimeScale.defaults); + +// node_modules/react-chartjs-2/dist/index.js +var import_react3 = __toESM(require_react(), 1); +var defaultDatasetIdKey = "label"; +function reforwardRef(ref, value) { + if (typeof ref === "function") { + ref(value); + } else if (ref) { + ref.current = value; + } +} +function setOptions(chart, nextOptions) { + const options = chart.options; + if (options && nextOptions) { + Object.assign(options, nextOptions); + } +} +function setLabels(currentData, nextLabels) { + currentData.labels = nextLabels; +} +function setDatasets(currentData, nextDatasets) { + let datasetIdKey = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : defaultDatasetIdKey; + const addedDatasets = []; + currentData.datasets = nextDatasets.map((nextDataset) => { + const currentDataset = currentData.datasets.find((dataset) => dataset[datasetIdKey] === nextDataset[datasetIdKey]); + if (!currentDataset || !nextDataset.data || addedDatasets.includes(currentDataset)) { + return { + ...nextDataset + }; } + addedDatasets.push(currentDataset); + Object.assign(currentDataset, nextDataset); + return currentDataset; + }); +} +function cloneData(data) { + let datasetIdKey = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : defaultDatasetIdKey; + const nextData = { + labels: [], + datasets: [] }; - const mountDependencies = (atom2, atomState, prevDependencies) => { - const depSet = new Set(atomState.d.keys()); - prevDependencies == null ? void 0 : prevDependencies.forEach((_, a) => { - if (depSet.has(a)) { - depSet.delete(a); - return; - } - const mounted = mountedMap.get(a); - if (mounted) { - mounted.t.delete(atom2); - if (canUnmountAtom(a, mounted)) { - unmountAtom(a); - } - } - }); - depSet.forEach((a) => { - const mounted = mountedMap.get(a); - if (mounted) { - mounted.t.add(atom2); - } else if (mountedMap.has(atom2)) { - mountAtom(a, atom2); - } + setLabels(nextData, data.labels); + setDatasets(nextData, data.datasets, datasetIdKey); + return nextData; +} +function ChartComponent(props, ref) { + const { height = 150, width = 300, redraw = false, datasetIdKey, type, data, options, plugins = [], fallbackContent, updateMode, ...canvasProps } = props; + const canvasRef = (0, import_react3.useRef)(null); + const chartRef = (0, import_react3.useRef)(); + const renderChart = () => { + if (!canvasRef.current) + return; + chartRef.current = new Chart(canvasRef.current, { + type, + data: cloneData(data, datasetIdKey), + options: options && { + ...options + }, + plugins }); + reforwardRef(ref, chartRef.current); }; - const flushPending = () => { - let flushed; - if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { - flushed = /* @__PURE__ */ new Set(); + const destroyChart = () => { + reforwardRef(ref, null); + if (chartRef.current) { + chartRef.current.destroy(); + chartRef.current = null; } - while (pendingMap.size) { - const pending = Array.from(pendingMap); - pendingMap.clear(); - pending.forEach(([atom2, prevAtomState]) => { - const atomState = getAtomState(atom2); - if (atomState) { - if (atomState.d !== (prevAtomState == null ? void 0 : prevAtomState.d)) { - mountDependencies(atom2, atomState, prevAtomState == null ? void 0 : prevAtomState.d); - } - const mounted = mountedMap.get(atom2); - if (mounted && !// TODO This seems pretty hacky. Hope to fix it. - // Maybe we could `mountDependencies` in `setAtomState`? - (prevAtomState && !hasPromiseAtomValue(prevAtomState) && (isEqualAtomValue(prevAtomState, atomState) || isEqualAtomError(prevAtomState, atomState)))) { - mounted.l.forEach((listener) => listener()); - if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { - flushed.add(atom2); - } - } - } else if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { - console.warn("[Bug] no atom state to flush"); - } - }); + }; + (0, import_react3.useEffect)(() => { + if (!redraw && chartRef.current && options) { + setOptions(chartRef.current, options); } - if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { - storeListenersRev1.forEach((l) => l("state")); - return flushed; + }, [ + redraw, + options + ]); + (0, import_react3.useEffect)(() => { + if (!redraw && chartRef.current) { + setLabels(chartRef.current.config.data, data.labels); } - }; - const subscribeAtom = (atom2, listener) => { - const mounted = addAtom(atom2); - const flushed = flushPending(); - const listeners = mounted.l; - listeners.add(listener); - if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { - storeListenersRev1.forEach((l) => l("sub")); - storeListenersRev2.forEach( - (l) => l({ type: "sub", flushed }) - ); + }, [ + redraw, + data.labels + ]); + (0, import_react3.useEffect)(() => { + if (!redraw && chartRef.current && data.datasets) { + setDatasets(chartRef.current.config.data, data.datasets, datasetIdKey); } - return () => { - listeners.delete(listener); - delAtom(atom2); - if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { - storeListenersRev1.forEach((l) => l("unsub")); - storeListenersRev2.forEach((l) => l({ type: "unsub" })); + }, [ + redraw, + data.datasets + ]); + (0, import_react3.useEffect)(() => { + if (!chartRef.current) + return; + if (redraw) { + destroyChart(); + setTimeout(renderChart); + } else { + chartRef.current.update(updateMode); + } + }, [ + redraw, + options, + data.labels, + data.datasets, + updateMode + ]); + (0, import_react3.useEffect)(() => { + if (!chartRef.current) + return; + destroyChart(); + setTimeout(renderChart); + }, [ + type + ]); + (0, import_react3.useEffect)(() => { + renderChart(); + return () => destroyChart(); + }, []); + return /* @__PURE__ */ import_react3.default.createElement("canvas", Object.assign({ + ref: canvasRef, + role: "img", + height, + width + }, canvasProps), fallbackContent); +} +var Chart2 = /* @__PURE__ */ (0, import_react3.forwardRef)(ChartComponent); +function createTypedChart(type, registerables) { + Chart.register(registerables); + return /* @__PURE__ */ (0, import_react3.forwardRef)((props, ref) => /* @__PURE__ */ import_react3.default.createElement(Chart2, Object.assign({}, props, { + ref, + type + }))); +} +var Line = /* @__PURE__ */ createTypedChart("line", LineController); + +// node_modules/jotai/esm/vanilla/utils.mjs +var RESET = Symbol(); +var isPromiseLike3 = (x) => typeof (x == null ? void 0 : x.then) === "function"; +function createJSONStorage(getStringStorage) { + let lastStr; + let lastValue; + const storage = { + getItem: (key, initialValue) => { + var _a, _b; + const parse2 = (str2) => { + str2 = str2 || ""; + if (lastStr !== str2) { + try { + lastValue = JSON.parse(str2); + } catch { + return initialValue; + } + lastStr = str2; + } + return lastValue; + }; + const str = (_b = (_a = getStringStorage()) == null ? void 0 : _a.getItem(key)) != null ? _b : null; + if (isPromiseLike3(str)) { + return str.then(parse2); } - }; + return parse2(str); + }, + setItem: (key, newValue) => { + var _a; + return (_a = getStringStorage()) == null ? void 0 : _a.setItem(key, JSON.stringify(newValue)); + }, + removeItem: (key) => { + var _a; + return (_a = getStringStorage()) == null ? void 0 : _a.removeItem(key); + } }; - if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { - return { - get: readAtom, - set: writeAtom, - sub: subscribeAtom, - // store dev methods (these are tentative and subject to change without notice) - dev_subscribe_store: (l, rev) => { - if (rev !== 2) { - console.warn( - "The current StoreListener revision is 2. The older ones are deprecated." - ); - storeListenersRev1.add(l); - return () => { - storeListenersRev1.delete(l); - }; - } - storeListenersRev2.add(l); + if (typeof window !== "undefined" && typeof window.addEventListener === "function") { + storage.subscribe = (key, callback2, initialValue) => { + if (!(getStringStorage() instanceof window.Storage)) { return () => { - storeListenersRev2.delete(l); }; - }, - dev_get_mounted_atoms: () => mountedAtoms.values(), - dev_get_atom_state: (a) => atomStateMap.get(a), - dev_get_mounted: (a) => mountedMap.get(a), - dev_restore_atoms: (values) => { - for (const [atom2, valueOrPromise] of values) { - if (hasInitialValue(atom2)) { - setAtomValueOrPromise(atom2, valueOrPromise); - recomputeDependents(atom2); + } + const storageEventCallback = (e) => { + if (e.storageArea === getStringStorage() && e.key === key) { + let newValue; + try { + newValue = JSON.parse(e.newValue || ""); + } catch { + newValue = initialValue; } + callback2(newValue); } - const flushed = flushPending(); - storeListenersRev2.forEach( - (l) => l({ type: "restore", flushed }) - ); - } + }; + window.addEventListener("storage", storageEventCallback); + return () => { + window.removeEventListener("storage", storageEventCallback); + }; }; } - return { - get: readAtom, - set: writeAtom, - sub: subscribeAtom - }; -}; -var defaultStore; -var getDefaultStore = () => { - if (!defaultStore) { - defaultStore = createStore(); - } - return defaultStore; -}; - -// node_modules/jotai/esm/react.mjs -var import_react2 = __toESM(require_react(), 1); -var StoreContext = (0, import_react2.createContext)(void 0); -var useStore = (options) => { - const store = (0, import_react2.useContext)(StoreContext); - return (options == null ? void 0 : options.store) || store || getDefaultStore(); -}; -var isPromiseLike2 = (x) => typeof (x == null ? void 0 : x.then) === "function"; -var use = import_react2.default.use || ((promise) => { - if (promise.status === "pending") { - throw promise; - } else if (promise.status === "fulfilled") { - return promise.value; - } else if (promise.status === "rejected") { - throw promise.reason; - } else { - promise.status = "pending"; - promise.then( - (v) => { - promise.status = "fulfilled"; - promise.value = v; + return storage; +} +var defaultStorage = createJSONStorage( + () => typeof window !== "undefined" ? window.localStorage : void 0 +); +var cache1$1 = /* @__PURE__ */ new WeakMap(); +var memo1 = (create, dep1) => (cache1$1.has(dep1) ? cache1$1 : cache1$1.set(dep1, create())).get(dep1); +var LOADING = { state: "loading" }; +function loadable(anAtom) { + return memo1(() => { + const loadableCache = /* @__PURE__ */ new WeakMap(); + const refreshAtom = atom(0); + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + refreshAtom.debugPrivate = true; + } + const derivedAtom = atom( + (get, { setSelf }) => { + get(refreshAtom); + const promise = get(anAtom); + if (!(promise instanceof Promise)) { + return { state: "hasData", data: promise }; + } + const cached = loadableCache.get(promise); + if (cached) { + return cached; + } + loadableCache.set(promise, LOADING); + promise.then( + (data) => { + loadableCache.set(promise, { state: "hasData", data }); + }, + (error) => { + loadableCache.set(promise, { state: "hasError", error }); + } + ).finally(setSelf); + return LOADING; }, - (e) => { - promise.status = "rejected"; - promise.reason = e; + (_get, set2) => { + set2(refreshAtom, (c) => c + 1); } ); - throw promise; - } -}); -function useAtomValue(atom2, options) { - const store = useStore(options); - const [[valueFromReducer, storeFromReducer, atomFromReducer], rerender] = (0, import_react2.useReducer)( - (prev) => { - const nextValue = store.get(atom2); - if (Object.is(prev[0], nextValue) && prev[1] === store && prev[2] === atom2) { - return prev; - } - return [nextValue, store, atom2]; - }, - void 0, - () => [store.get(atom2), store, atom2] - ); - let value = valueFromReducer; - if (storeFromReducer !== store || atomFromReducer !== atom2) { - rerender(); - value = store.get(atom2); - } - const delay = options == null ? void 0 : options.delay; - (0, import_react2.useEffect)(() => { - const unsub = store.sub(atom2, () => { - if (typeof delay === "number") { - setTimeout(rerender, delay); - return; - } - rerender(); - }); - rerender(); - return unsub; - }, [store, atom2, delay]); - (0, import_react2.useDebugValue)(value); - return isPromiseLike2(value) ? use(value) : value; -} -function useSetAtom(atom2, options) { - const store = useStore(options); - const setAtom = (0, import_react2.useCallback)( - (...args) => { - if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production" && !("write" in atom2)) { - throw new Error("not writable atom"); - } - return store.set(atom2, ...args); - }, - [store, atom2] - ); - return setAtom; -} -function useAtom(atom2, options) { - return [ - useAtomValue(atom2, options), - // We do wrong type assertion here, which results in throwing an error. - useSetAtom(atom2, options) - ]; + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + derivedAtom.debugPrivate = true; + } + return atom((get) => get(derivedAtom)); + }, anAtom); } -// src/Picker.tsx +// src/CalendarPricesChart.tsx +var import_react4 = __toESM(require_react(), 1); var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1); -function Picker({ - $url, - $options = (0, import_react3.useMemo)(() => atom([]), []), - $isLoading = (0, import_react3.useMemo)(() => atom(true), []), - $isEnabled = (0, import_react3.useMemo)(() => atom(true), []), - $selectedOption = (0, import_react3.useMemo)(() => atom(""), []) -}) { - const url = useAtomValue($url); - const options = useAtomValue($options); - const isLoading = useAtomValue($isLoading); - const [selectedOption, setSelectedOption] = useAtom($selectedOption); - const isEnabled = useAtomValue($isEnabled); - const optionsFetched = useSetAtom((0, import_react3.useMemo)(() => atom(null, (get, set2, options2) => { - set2($options, options2); - set2($isLoading, false); - }), [$options, $isLoading])); - (0, import_react3.useEffect)(() => { - if (isEnabled) { - fetch(url).then((x) => x.json()).catch((err) => ["AAPL", "MSFT", "GOOG"]).then(optionsFetched); - } - }, [url, isEnabled]); - return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Loading..." }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { value: selectedOption, onChange: (e) => { - setSelectedOption(e.target.value); - }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "" }, ""), - options.map( - (date) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: date, children: date }, date) - ) - ] }) }); +Chart.register(LineElement, plugin_tooltip, plugin_legend, CategoryScale, LinearScale, PointElement); +var $prices = loadable(atom(async (get) => { + const selectedUnderlying = get($selectedUnderlying); + const selectedStrike = get($selectedStrike); + const selectedFrontExpiration = get($selectedFrontExpiration); + const selectedBackExpiration = get($selectedBackExpiration); + if (selectedUnderlying !== "" && selectedStrike !== "" && selectedFrontExpiration !== "" && selectedBackExpiration !== "") { + return await fetch(`${baseUrl}/option_quotes/${selectedUnderlying}/${selectedStrike}/${selectedFrontExpiration}/${selectedBackExpiration}`).then((x) => x.json()).catch(() => []); + } else { + return []; + } +})); +function CalendarPricesChart() { + const prices = useAtomValue($prices); + (0, import_react4.useEffect)(() => { + console.log(prices); + }, [prices]); + if (prices.state === "hasData") { + return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + Line, + { + datasetIdKey: "id", + data: { + labels: prices.data.map((x) => x.quote_date.substring(0, 10)), + datasets: [ + { + //@ts-ignore + id: 1, + label: "", + data: prices.data.map((x) => x.price) + } + ] + } + } + ) }); + } else { + return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, {}); + } } -// src/App.module.css -var App_module_default = { - "app": "App-module__app_gPMrEW__001", - "picker": "App-module__picker_gPMrEW__001" -}; - // src/App.tsx var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1); var baseUrl = "http://127.0.0.1:8234"; @@ -36722,19 +36843,7 @@ var $selectedFrontExpiration = atom(""); var $backMonthExpirationPickerUrl = atom((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/${get($selectedQuoteDate)}/expirations`); var $isBackMonthExpirationPickerEnabled = atom((get) => get($selectedQuoteDate) !== "" && get($selectedUnderlying) !== ""); var $selectedBackExpiration = atom(""); -var $prices = atom((get) => { - const selectedUnderlying = get($selectedUnderlying); - const selectedStrike = get($selectedStrike); - const selectedFrontExpiration = get($selectedFrontExpiration); - const selectedBackExpiration = get($selectedBackExpiration); - if (selectedUnderlying !== "" && selectedStrike !== "" && selectedFrontExpiration !== "" && selectedBackExpiration !== "") { - return fetch(`${baseUrl}/option_quotes/${selectedUnderlying}/${selectedStrike}/${selectedFrontExpiration}/${selectedBackExpiration}`).then((x) => x.json()).catch(() => []); - } else { - return Promise.resolve([]); - } -}); function App() { - const prices = useAtomValue($prices); return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: App_module_default.app, children: [ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Header_default, {}), /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: App_module_default.picker, children: [ @@ -36757,7 +36866,7 @@ function App() { /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { children: "Back Expiration" }), /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Picker, { $url: $backMonthExpirationPickerUrl, $isEnabled: $isBackMonthExpirationPickerEnabled, $selectedOption: $selectedBackExpiration }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HistoricalImpliedVolatilityChart, {}) + /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CalendarPricesChart, {}) ] }); } var App_default = App; diff --git a/src/App.tsx b/src/App.tsx index 8a8e750..a6f7325 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,48 +2,38 @@ import Header from './Header'; import { HistoricalImpliedVolatilityChart } from "./HistoricalImpliedVolatilityChart"; import { Picker } from './Picker'; import { atom as $, useAtomValue } from 'jotai'; +import { loadable } from 'jotai/utils'; //import './index.css'; //@ts-ignore import k from './App.module.css'; +import { useEffect } from 'react'; +import { CalendarPricesChart } from './CalendarPricesChart'; -const baseUrl = 'http://127.0.0.1:8234'; +export const baseUrl = 'http://127.0.0.1:8234'; /* The following are wrapped in atoms to prevent re-creating them if App() re-runs. */ -const $underlyingsUrl = $(`${baseUrl}/option_quotes/underlyings`); -const $selectedUnderlying = $(''); - -const $quoteDatePickerUrl = $((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/quote_dates`); -const $isQuoteDatePickerEnabled = $((get) => get($selectedUnderlying)!==''); -const $selectedQuoteDate = $(''); - -const $strikePickerUrl = $((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/${get($selectedQuoteDate)}/strikes`); -const $isStrikePickerEnabled = $((get) => get($selectedQuoteDate)!=='' && get($selectedUnderlying)!==''); -const $selectedStrike = $(''); - -const $frontMonthExpirationPickerUrl = $((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/${get($selectedQuoteDate)}/expirations`); -const $isFrontMonthExpirationPickerEnabled = $((get) => get($selectedQuoteDate)!=='' && get($selectedUnderlying)!==''); -const $selectedFrontExpiration = $(''); - -const $backMonthExpirationPickerUrl = $((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/${get($selectedQuoteDate)}/expirations`); -const $isBackMonthExpirationPickerEnabled = $((get) => get($selectedQuoteDate)!=='' && get($selectedUnderlying)!==''); -const $selectedBackExpiration = $(''); - -const $prices = $((get)=>{ - const selectedUnderlying = get($selectedUnderlying); - const selectedStrike = get($selectedStrike); - const selectedFrontExpiration = get($selectedFrontExpiration); - const selectedBackExpiration = get($selectedBackExpiration); - if(selectedUnderlying!=='' && selectedStrike!=='' && selectedFrontExpiration!=='' && selectedBackExpiration!==''){ - return fetch(`${baseUrl}/option_quotes/${selectedUnderlying}/${selectedStrike}/${selectedFrontExpiration}/${selectedBackExpiration}`).then(x=>x.json()).catch(()=>[]); - } - else{ - return Promise.resolve([]); - } -}); +export const $underlyingsUrl = $(`${baseUrl}/option_quotes/underlyings`); +export const $selectedUnderlying = $(''); -function App() { - const prices = useAtomValue($prices); +export const $quoteDatePickerUrl = $((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/quote_dates`); +export const $isQuoteDatePickerEnabled = $((get) => get($selectedUnderlying)!==''); +export const $selectedQuoteDate = $(''); + +export const $strikePickerUrl = $((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/${get($selectedQuoteDate)}/strikes`); +export const $isStrikePickerEnabled = $((get) => get($selectedQuoteDate)!=='' && get($selectedUnderlying)!==''); +export const $selectedStrike = $(''); + +export const $frontMonthExpirationPickerUrl = $((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/${get($selectedQuoteDate)}/expirations`); +export const $isFrontMonthExpirationPickerEnabled = $((get) => get($selectedQuoteDate)!=='' && get($selectedUnderlying)!==''); +export const $selectedFrontExpiration = $(''); + +export const $backMonthExpirationPickerUrl = $((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/${get($selectedQuoteDate)}/expirations`); +export const $isBackMonthExpirationPickerEnabled = $((get) => get($selectedQuoteDate)!=='' && get($selectedUnderlying)!==''); +export const $selectedBackExpiration = $(''); + + +function App() { return (
@@ -52,7 +42,8 @@ function App() {
- + + {/* */}
); } diff --git a/src/CalendarPricesChart.tsx b/src/CalendarPricesChart.tsx new file mode 100644 index 0000000..c449f01 --- /dev/null +++ b/src/CalendarPricesChart.tsx @@ -0,0 +1,52 @@ +import { Chart as ChartJS, Tooltip, Legend, LineElement, CategoryScale, LinearScale, PointElement } from "chart.js"; +import { Line } from 'react-chartjs-2'; +import { atom as $, useAtomValue } from 'jotai'; +import { loadable } from 'jotai/utils'; +import { useEffect } from "react"; +import { baseUrl, $selectedUnderlying, $selectedStrike, $selectedFrontExpiration, $selectedBackExpiration } from "./App"; + +ChartJS.register(LineElement, Tooltip, Legend, CategoryScale, LinearScale, PointElement); + +export type CalendarPrice = { + quote_date: string, + price: number +}; + +const $prices = loadable>>($(async (get)=>{ + const selectedUnderlying = get($selectedUnderlying); + const selectedStrike = get($selectedStrike); + const selectedFrontExpiration = get($selectedFrontExpiration); + const selectedBackExpiration = get($selectedBackExpiration); + if(selectedUnderlying!=='' && selectedStrike!=='' && selectedFrontExpiration!=='' && selectedBackExpiration!==''){ + return await fetch(`${baseUrl}/option_quotes/${selectedUnderlying}/${selectedStrike}/${selectedFrontExpiration}/${selectedBackExpiration}`).then(x=>x.json()).catch(()=>[]); + } + else{ + return []; + } +})); + +export function CalendarPricesChart(){ + const prices = useAtomValue($prices); + + useEffect(()=>{ console.log(prices); },[prices]) + if(prices.state === 'hasData'){ + return (
+ x.quote_date.substring(0,10)), + datasets: [ + {//@ts-ignore + id: 1, + label: '', + data: prices.data.map((x)=>x.price), + }, + ], + }} + /> +
); + } + else { + return (<>); + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index a31b89b..6d46ab6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { - "jsx": "react-jsx" + "jsx": "react-jsx", + "lib": ["DOM", "DOM.Iterable", "ES2017"] }, } \ No newline at end of file