UNPKG

taxonium-component

Version:

React component for exploring large phylogenetic trees in the browser

659 lines (658 loc) 24.3 kB
import { jsx as a, jsxs as $, Fragment as P } from "react/jsx-runtime"; import * as C from "react"; import { useState as S, Suspense as se, lazy as V } from "react"; import { z as I, A as H, au as ie, E as U, G as Q, aQ as L, H as J, a$ as le, I as K, aR as de, bv as ce, b_ as W, aN as ue, T as _, u as X, c as pe, b$ as fe, c0 as ge, a as ve, o as Y, P as me, aG as he } from "./JBrowsePanel-BNE3gNW1.js"; import { F as be } from "./FeatureDetails-BEUoAeO7.js"; import { B as q } from "./BaseCard-uve71KMy.js"; import { D as Z } from "./DataGridFlexContainer-fD_l1wuy.js"; import { F as ee } from "./FormControlLabel-CfHqydvH.js"; import { C as te } from "./Checkbox-D7_LksK2.js"; import { D as oe } from "./DataGrid-cokHFdGI.js"; import { p as E, c as ne } from "./index-CoM8QAjP.js"; import r from "prop-types"; function ye(e) { return C.Children.toArray(e).filter((t) => /* @__PURE__ */ C.isValidElement(t)); } function Ce(e) { return H("MuiToggleButton", e); } const R = I("MuiToggleButton", ["root", "disabled", "selected", "standard", "primary", "secondary", "sizeSmall", "sizeMedium", "sizeLarge", "fullWidth"]), j = /* @__PURE__ */ C.createContext({}); E.env.NODE_ENV !== "production" && (j.displayName = "ToggleButtonGroupContext"); const M = /* @__PURE__ */ C.createContext(void 0); E.env.NODE_ENV !== "production" && (M.displayName = "ToggleButtonGroupButtonContext"); function Te(e, t) { return t === void 0 || e === void 0 ? !1 : Array.isArray(t) ? t.includes(e) : e === t; } const Be = (e) => { const { classes: t, fullWidth: o, selected: n, disabled: d, size: s, color: c } = e, l = { root: ["root", n && "selected", d && "disabled", o && "fullWidth", `size${L(s)}`, c] }; return J(l, Ce, t); }, $e = Q(le, { name: "MuiToggleButton", slot: "Root", overridesResolver: (e, t) => { const { ownerState: o } = e; return [t.root, t[`size${L(o.size)}`]]; } })(K(({ theme: e }) => ({ ...e.typography.button, borderRadius: (e.vars || e).shape.borderRadius, padding: 11, border: `1px solid ${(e.vars || e).palette.divider}`, color: (e.vars || e).palette.action.active, [`&.${R.disabled}`]: { color: (e.vars || e).palette.action.disabled, border: `1px solid ${(e.vars || e).palette.action.disabledBackground}` }, "&:hover": { textDecoration: "none", // Reset on mouse devices backgroundColor: e.alpha((e.vars || e).palette.text.primary, (e.vars || e).palette.action.hoverOpacity), "@media (hover: none)": { backgroundColor: "transparent" } }, variants: [{ props: { color: "standard" }, style: { [`&.${R.selected}`]: { color: (e.vars || e).palette.text.primary, backgroundColor: e.alpha((e.vars || e).palette.text.primary, (e.vars || e).palette.action.selectedOpacity), "&:hover": { backgroundColor: e.alpha((e.vars || e).palette.text.primary, `${(e.vars || e).palette.action.selectedOpacity} + ${(e.vars || e).palette.action.hoverOpacity}`), // Reset on touch devices, it doesn't add specificity "@media (hover: none)": { backgroundColor: e.alpha((e.vars || e).palette.text.primary, (e.vars || e).palette.action.selectedOpacity) } } } } }, ...Object.entries(e.palette).filter(de()).map(([t]) => ({ props: { color: t }, style: { [`&.${R.selected}`]: { color: (e.vars || e).palette[t].main, backgroundColor: e.alpha((e.vars || e).palette[t].main, (e.vars || e).palette.action.selectedOpacity), "&:hover": { backgroundColor: e.alpha((e.vars || e).palette[t].main, `${(e.vars || e).palette.action.selectedOpacity} + ${(e.vars || e).palette.action.hoverOpacity}`), // Reset on touch devices, it doesn't add specificity "@media (hover: none)": { backgroundColor: e.alpha((e.vars || e).palette[t].main, (e.vars || e).palette.action.selectedOpacity) } } } } })), { props: { fullWidth: !0 }, style: { width: "100%" } }, { props: { size: "small" }, style: { padding: 7, fontSize: e.typography.pxToRem(13) } }, { props: { size: "large" }, style: { padding: 15, fontSize: e.typography.pxToRem(15) } }] }))), A = /* @__PURE__ */ C.forwardRef(function(t, o) { const { value: n, ...d } = C.useContext(j), s = C.useContext(M), c = ie({ ...d, selected: Te(t.value, n) }, t), l = U({ props: c, name: "MuiToggleButton" }), { children: i, className: f, color: p = "standard", disabled: g = !1, disableFocusRipple: b = !1, fullWidth: m = !1, onChange: w, onClick: x, selected: T, size: O = "medium", value: N, ...G } = l, v = { ...l, color: p, disabled: g, disableFocusRipple: b, fullWidth: m, size: O }, B = Be(v), F = (h) => { x && (x(h, N), h.defaultPrevented) || w && w(h, N); }, y = s || ""; return /* @__PURE__ */ a($e, { className: ne(d.className, B.root, f, y), disabled: g, focusRipple: !b, ref: o, onClick: F, onChange: w, value: N, ownerState: v, "aria-pressed": T, ...G, children: i }); }); E.env.NODE_ENV !== "production" && (A.propTypes = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ // └─────────────────────────────────────────────────────────────────────┘ /** * The content of the component. */ children: r.node, /** * Override or extend the styles applied to the component. */ classes: r.object, /** * @ignore */ className: r.string, /** * The color of the button when it is in an active state. * It supports both default and custom theme colors, which can be added as shown in the * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). * @default 'standard' */ color: r.oneOfType([r.oneOf(["standard", "primary", "secondary", "error", "info", "success", "warning"]), r.string]), /** * If `true`, the component is disabled. * @default false */ disabled: r.bool, /** * If `true`, the keyboard focus ripple is disabled. * @default false */ disableFocusRipple: r.bool, /** * If `true`, the ripple effect is disabled. * * ⚠️ Without a ripple there is no styling for :focus-visible by default. Be sure * to highlight the element by applying separate styles with the `.Mui-focusVisible` class. * @default false */ disableRipple: r.bool, /** * If `true`, the button will take up the full width of its container. * @default false */ fullWidth: r.bool, /** * Callback fired when the state changes. * * @param {React.MouseEvent<HTMLElement>} event The event source of the callback. * @param {any} value of the selected button. */ onChange: r.func, /** * Callback fired when the button is clicked. * * @param {React.MouseEvent<HTMLElement>} event The event source of the callback. * @param {any} value of the selected button. */ onClick: r.func, /** * If `true`, the button is rendered in an active state. */ selected: r.bool, /** * The size of the component. * The prop defaults to the value inherited from the parent ToggleButtonGroup component. * @default 'medium' */ size: r.oneOfType([r.oneOf(["small", "medium", "large"]), r.string]), /** * The system prop that allows defining system overrides as well as additional CSS styles. */ sx: r.oneOfType([r.arrayOf(r.oneOfType([r.func, r.object, r.bool])), r.func, r.object]), /** * The value to associate with the button when selected in a * ToggleButtonGroup. */ value: r.any.isRequired }); function Oe(e) { return H("MuiToggleButtonGroup", e); } const u = I("MuiToggleButtonGroup", ["root", "selected", "horizontal", "vertical", "disabled", "grouped", "groupedHorizontal", "groupedVertical", "fullWidth", "firstButton", "lastButton", "middleButton"]), we = (e) => { const { classes: t, orientation: o, fullWidth: n, disabled: d } = e, s = { root: ["root", o, n && "fullWidth"], grouped: ["grouped", `grouped${L(o)}`, d && "disabled"], firstButton: ["firstButton"], lastButton: ["lastButton"], middleButton: ["middleButton"] }; return J(s, Oe, t); }, xe = Q("div", { name: "MuiToggleButtonGroup", slot: "Root", overridesResolver: (e, t) => { const { ownerState: o } = e; return [{ [`& .${u.grouped}`]: t.grouped }, { [`& .${u.grouped}`]: t[`grouped${L(o.orientation)}`] }, { [`& .${u.firstButton}`]: t.firstButton }, { [`& .${u.lastButton}`]: t.lastButton }, { [`& .${u.middleButton}`]: t.middleButton }, t.root, o.orientation === "vertical" && t.vertical, o.fullWidth && t.fullWidth]; } })(K(({ theme: e }) => ({ display: "inline-flex", borderRadius: (e.vars || e).shape.borderRadius, variants: [{ props: { orientation: "vertical" }, style: { flexDirection: "column", [`& .${u.grouped}`]: { [`&.${u.selected} + .${u.grouped}.${u.selected}`]: { borderTop: 0, marginTop: 0 } }, [`& .${u.firstButton},& .${u.middleButton}`]: { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }, [`& .${u.lastButton},& .${u.middleButton}`]: { marginTop: -1, borderTop: "1px solid transparent", borderTopLeftRadius: 0, borderTopRightRadius: 0 }, [`& .${u.lastButton}.${R.disabled},& .${u.middleButton}.${R.disabled}`]: { borderTop: "1px solid transparent" } } }, { props: { fullWidth: !0 }, style: { width: "100%" } }, { props: { orientation: "horizontal" }, style: { [`& .${u.grouped}`]: { [`&.${u.selected} + .${u.grouped}.${u.selected}`]: { borderLeft: 0, marginLeft: 0 } }, [`& .${u.firstButton},& .${u.middleButton}`]: { borderTopRightRadius: 0, borderBottomRightRadius: 0 }, [`& .${u.lastButton},& .${u.middleButton}`]: { marginLeft: -1, borderLeft: "1px solid transparent", borderTopLeftRadius: 0, borderBottomLeftRadius: 0 }, [`& .${u.lastButton}.${R.disabled},& .${u.middleButton}.${R.disabled}`]: { borderLeft: "1px solid transparent" } } }] }))), re = /* @__PURE__ */ C.forwardRef(function(t, o) { const n = U({ props: t, name: "MuiToggleButtonGroup" }), { children: d, className: s, color: c = "standard", disabled: l = !1, exclusive: i = !1, fullWidth: f = !1, onChange: p, orientation: g = "horizontal", size: b = "medium", value: m, ...w } = n, x = { ...n, disabled: l, fullWidth: f, orientation: g, size: b }, T = we(x), O = C.useCallback((y, h) => { if (!p) return; const k = m && m.indexOf(h); let D; m && k >= 0 ? (D = m.slice(), D.splice(k, 1)) : D = m ? m.concat(h) : [h], p(y, D); }, [p, m]), N = C.useCallback((y, h) => { p && p(y, m === h ? null : h); }, [p, m]), G = C.useMemo(() => ({ className: T.grouped, onChange: i ? N : O, value: m, size: b, fullWidth: f, color: c, disabled: l }), [T.grouped, i, N, O, m, b, f, c, l]), v = ye(d), B = v.length, F = (y) => { const h = y === 0, k = y === B - 1; return h && k ? "" : h ? T.firstButton : k ? T.lastButton : T.middleButton; }; return /* @__PURE__ */ a(xe, { role: "group", className: ne(T.root, s), ref: o, ownerState: x, ...w, children: /* @__PURE__ */ a(j.Provider, { value: G, children: v.map((y, h) => (E.env.NODE_ENV !== "production" && ce.isFragment(y) && console.error(["MUI: The ToggleButtonGroup component doesn't accept a Fragment as a child.", "Consider providing an array instead."].join(` `)), /* @__PURE__ */ a(M.Provider, { value: F(h), children: y }, h))) }) }); }); E.env.NODE_ENV !== "production" && (re.propTypes = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ // └─────────────────────────────────────────────────────────────────────┘ /** * The content of the component. */ children: r.node, /** * Override or extend the styles applied to the component. */ classes: r.object, /** * @ignore */ className: r.string, /** * The color of the button when it is selected. * It supports both default and custom theme colors, which can be added as shown in the * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). * @default 'standard' */ color: r.oneOfType([r.oneOf(["standard", "primary", "secondary", "error", "info", "success", "warning"]), r.string]), /** * If `true`, the component is disabled. This implies that all ToggleButton children will be disabled. * @default false */ disabled: r.bool, /** * If `true`, only allow one of the child ToggleButton values to be selected. * @default false */ exclusive: r.bool, /** * If `true`, the button group will take up the full width of its container. * @default false */ fullWidth: r.bool, /** * Callback fired when the value changes. * * @param {React.MouseEvent<HTMLElement>} event The event source of the callback. * @param {any} value of the selected buttons. When `exclusive` is true * this is a single value; when false an array of selected values. If no value * is selected and `exclusive` is true the value is null; when false an empty array. */ onChange: r.func, /** * The component orientation (layout flow direction). * @default 'horizontal' */ orientation: r.oneOf(["horizontal", "vertical"]), /** * The size of the component. * @default 'medium' */ size: r.oneOfType([r.oneOf(["small", "medium", "large"]), r.string]), /** * The system prop that allows defining system overrides as well as additional CSS styles. */ sx: r.oneOfType([r.arrayOf(r.oneOfType([r.func, r.object, r.bool])), r.func, r.object]), /** * The currently selected value within the group or an array of selected * values when `exclusive` is false. * * The value must have reference equality with the option in order to be selected. */ value: r.any }); function Ne({ value: e, ref: t }) { const [o, n] = S(!1); return W(t, e) !== e ? $("div", { children: [a("button", { onClick: () => { n(!o); }, children: o ? "Show simplified ALT" : "Show raw ALT" }), " ", o ? e : W(t, e)] }) : e; } function Fe({ value: e }) { const [t, o] = S(!1), [n, d] = S(!1), s = String(e); return s.length > 100 ? $(P, { children: [a("button", { type: "button", onClick: () => { ue(s), d(!0), setTimeout(() => { d(!1); }, 700); }, children: n ? "Copied to clipboard" : "Copy" }), a("button", { type: "button", onClick: () => { o((c) => !c); }, children: t ? "Show less" : "Show more" }), a("div", { children: t ? s : `${s.slice(0, 100)}...` })] }) : a("div", { children: s }); } function Re({ checked: e, disabled: t, label: o, onChange: n }) { return a(ee, { disabled: t, control: a(te, { checked: e, onChange: n }), label: a(_, { variant: "body2", children: o }) }); } function Se(e) { return e.toPrecision(3); } function Ge({ rows: e }) { const [t, o] = S(!1), n = {}; if (t) for (const l of e) { const i = {}, f = l.GT.split(/[/|]/); for (const g of f) i[g] = (i[g] || 0) + 1; const p = Object.entries(i).map(([g, b]) => `${g}:${b}`).join(";"); n[p] || (n[p] = { count: 0, GT: p, genotype: l.genotype }), n[p].count++; } else for (const l of e) { const i = l.GT; n[i] || (n[i] = { count: 0, GT: l.GT, genotype: l.genotype }), n[i].count++; } const d = Object.entries(n).map(([l, i]) => ({ id: l, ...i, count: `${i.count} / ${e.length}`, frequency: `${Se(i.count / e.length * 100)}%` })), c = (d[0] ? Object.keys(d[0]) : []).map((l) => X.measureGridWidth(d.map((i) => `${i[l]}`))); return $("div", { children: [a(ee, { control: a(te, { checked: t }), label: a(_, { variant: "body2", children: "Use allele counts instead of exact GT" }), onChange: (l, i) => { o(i); } }), a(Z, { children: a(oe, { rows: d, hideFooter: !0, rowHeight: 25, columnHeaderHeight: 35, columns: [ { field: "GT", width: c[0] }, { field: "count", width: c[1] }, { field: "frequency", width: c[2] }, { field: "genotype", width: c[3] } ] }) })] }); } function ke({ columns: e, filter: t, setFilter: o }) { return $(P, { children: [a(_, { children: "These filters can use a plain text search or regex style query, e.g. in the genotype field, entering 1 will query for all genotypes that include the first alternate allele e.g. 0|1 or 1|1, entering [1-9]\\d* will find any non-zero allele e.g. 0|2 or 2/33" }), e.map(({ field: n }) => a(pe, { placeholder: `Filter ${n}`, value: t[n] || "", onChange: (d) => { o({ ...t, [n]: d.target.value }); } }, `filter-${n}`))] }); } function Ee(e, t, o, n) { const d = Object.entries(e).map(([i, f]) => { var p; const g = (p = f.GT) === null || p === void 0 ? void 0 : p[0]; return [ i, { ...f, ...g ? { GT: `${g}`, genotype: fe(`${g}`, t, o) } : {} } ]; }); let s, c = []; const l = Object.keys(n); try { c = d.map(([i, f]) => ({ ...Object.fromEntries(Object.entries(f).map(([p, g]) => [ p, g ])), sample: i, id: i })).filter((i) => l.length ? l.every((f) => { const p = n[f]; return p ? new RegExp(p, "i").exec(i[f]) : !0; }) : !0); } catch (i) { console.error(i), s = i; } return { rows: c, error: s }; } function De(e) { const { feature: t, descriptions: o = {} } = e, [n, d] = S({}), [s, c] = S("all"), [l, i] = S(!1), f = t.samples || {}, p = t.ALT, g = t.REF, { rows: b, error: m } = Ee(f, g, p, n), w = /* @__PURE__ */ new Set(["sample", ...Object.keys(b[0] || {})]); w.delete("id"); const x = [...w], T = x.map((v) => X.measureGridWidth(b.map((B) => B[v]))), O = x.map((v, B) => { var F, y; return { field: v, description: (y = (F = o == null ? void 0 : o.FORMAT) === null || F === void 0 ? void 0 : F[v]) === null || y === void 0 ? void 0 : y.Description, width: T[B] }; }), N = /* @__PURE__ */ new Set(["sample", "GT"]), G = /* @__PURE__ */ new Set(["sample", "GT", "genotype"]); return b.length ? $(P, { children: [a(q, { ...e, title: "Genotype frequencies", children: a(ge.ErrorBoundary, { FallbackComponent: ve.ErrorMessage, children: a(Ge, { rows: b }) }) }), $(q, { ...e, title: "Samples", children: [m ? a(_, { color: "error", children: `${m}` }) : null, $("div", { children: [a(Re, { label: "Show filters", checked: l, onChange: (v) => { i(v.target.checked); } }), $(re, { value: s, exclusive: !0, size: "small", onChange: (v, B) => { B !== null && c(B); }, children: [a(A, { value: "all", children: "All" }), a(A, { value: "gtOnly", children: "GT only" }), a(A, { value: "genotypeOnly", children: "GT+resolved genotype" })] })] }), l ? a(ke, { setFilter: d, columns: O, filter: n }) : null, a(Z, { children: a(oe, { rows: b, hideFooter: b.length < 100, columns: s === "gtOnly" ? O.filter((v) => N.has(v.field)) : s === "genotypeOnly" ? O.filter((v) => G.has(v.field)) : O, rowHeight: 25, columnHeaderHeight: 35, showToolbar: !0 }) })] })] }) : null; } const ze = { CHROM: "chromosome: An identifier from the reference genome", POS: "position: The reference position, with the 1st base having position 1", ID: "identifier: Semi-colon separated list of unique identifiers where available", REF: "reference base(s): Each base must be one of A,C,G,T,N (case insensitive).", ALT: "alternate base(s): Comma-separated list of alternate non-reference alleles", QUAL: "quality: Phred-scaled quality score for the assertion made in ALT", FILTER: "filter status: PASS if this position has passed all filters, otherwise a semicolon-separated list of codes for filters that fail" }, z = V(() => import("./LaunchBreakendPanel-DlICa3xR.js")), ae = V(() => import("./VariantConsequenceDataGrid-Cbj_NF6D.js")); function Ae({ descriptions: e, feature: t }) { var o, n, d, s, c; const l = (n = (o = e == null ? void 0 : e.INFO) === null || o === void 0 ? void 0 : o.ANN) === null || n === void 0 ? void 0 : n.Description, i = ((s = (d = l == null ? void 0 : l.match(/.*Functional annotations:'(.*)'$/)) === null || d === void 0 ? void 0 : d[1]) === null || s === void 0 ? void 0 : s.split("|")) || [], f = ((c = t.INFO) === null || c === void 0 ? void 0 : c.ANN) || []; return a(ae, { fields: i, data: f, title: "Variant ANN field" }); } function Le({ descriptions: e, feature: t }) { var o, n, d, s, c; const l = (n = (o = e == null ? void 0 : e.INFO) === null || o === void 0 ? void 0 : o.CSQ) === null || n === void 0 ? void 0 : n.Description, i = ((s = (d = l == null ? void 0 : l.match(/.*Format: (.*)/)) === null || d === void 0 ? void 0 : d[1]) === null || s === void 0 ? void 0 : s.split("|")) || [], f = ((c = t.INFO) === null || c === void 0 ? void 0 : c.CSQ) || []; return a(ae, { fields: i, data: f, title: "Variant CSQ field" }); } function _e({ model: e }) { const { featureData: t } = e, o = JSON.parse(JSON.stringify(t)), { type: n = "" } = o; return n === "breakend" ? a(z, { feature: o, locStrings: o.ALT.map((d) => { var s; return ((s = he(d)) === null || s === void 0 ? void 0 : s.MatePosition) || ""; }), model: e }) : n === "translocation" ? a(z, { feature: o, model: e, locStrings: [`${o.INFO.CHR2[0]}:${o.INFO.END}`] }) : n === "paired_feature" ? a(z, { feature: o, model: e, locStrings: [`${o.mate.refName}:${o.mate.start}`] }) : n.includes("inversion") || n.includes("deletion") || n.includes("duplication") || n.includes("cnv") || n.includes("sv") ? a(z, { feature: { uniqueId: "random", refName: o.refName, start: o.start, end: o.start + 1, mate: { refName: o.refName, start: o.end, end: o.end + 1 } }, model: e, locStrings: [`${o.refName}:${o.end}`] }) : null; } const Pe = Y(function(e) { const { feat: t, model: o } = e, { descriptions: n } = o, { samples: d, ...s } = t, { REF: c } = s; return $(me, { "data-testid": "variant-side-drawer", children: [a(be, { feature: s, descriptions: { ...ze, ...n }, formatter: (l, i) => i === "ALT" ? a(Ne, { value: `${l}`, ref: c }) : a(Fe, { value: l }), ...e }), $(se, { fallback: null, children: [a(Le, { feature: s, descriptions: n }), a(Ae, { feature: s, descriptions: n }), a(_e, { model: o })] }), a(De, { feature: t, ...e, descriptions: n })] }); }), Xe = Y(function(e) { const { model: t } = e, { featureData: o } = t, n = structuredClone(o); return n ? a(Pe, { feat: n, ...e }) : a("div", { children: "No feature loaded, may not be available after page refresh because it was too large for localStorage" }); }); export { Xe as default }; //# sourceMappingURL=VariantFeatureWidget-C_HTn85S.js.map