diff --git a/app/src/App.tsx b/app/src/App.tsx index 8ce26b50..94af6028 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -28,14 +28,16 @@ onSnapshot(mymodel, snap => { }) // used in ViewContainer files to get the width -export function useWidthSetter(view: { setWidth: (arg: number) => void }) { +function useWidthSetter(view: { setWidth: (arg: number) => void }) { const [ref, { width }] = useMeasure() useEffect(() => { if (width && isAlive(view)) { // sets after a requestAnimationFrame // https://stackoverflow.com/a/58701523/2129219 avoids ResizeObserver // loop error being shown during development - requestAnimationFrame(() => view.setWidth(width)) + requestAnimationFrame(() => { + view.setWidth(width) + }) } }, [view, width]) return ref diff --git a/eslint.config.mjs b/eslint.config.mjs index e6ef596b..5a524b77 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,60 +1,26 @@ -import { fixupConfigRules } from '@eslint/compat' -import globals from 'globals' -import tsParser from '@typescript-eslint/parser' -import path from 'node:path' -import { fileURLToPath } from 'node:url' -import js from '@eslint/js' -import { FlatCompat } from '@eslint/eslintrc' +import eslint from '@eslint/js' +import eslintPluginReact from 'eslint-plugin-react' +import eslintPluginReactHooks from 'eslint-plugin-react-hooks' +import eslintPluginReactRefresh from 'eslint-plugin-react-refresh' +import eslintPluginUnicorn from 'eslint-plugin-unicorn' +import tseslint from 'typescript-eslint' -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) -const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended, - allConfig: js.configs.all, -}) - -export default [ +export default tseslint.config( { ignores: [ - '**/coverage', - '**/node_modules/', - '**/dist/', - '**/bundle/', - '**/.eslintrc.js', + '**/build/**/*', + '**/dist/**/*', + '**/esm/**/*', + '**/public/**/*', + '**/bundle/**/*', 'lib/output-version.js', ], }, - ...fixupConfigRules( - compat.extends( - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:@typescript-eslint/recommended-type-checked', - 'plugin:@typescript-eslint/stylistic-type-checked', - 'plugin:unicorn/recommended', - 'plugin:react/recommended', - 'plugin:react-hooks/recommended', - 'plugin:prettier/recommended', - ), - ), { languageOptions: { - globals: { - ...globals.browser, - ...globals.node, - ...globals.jest, - }, - - parser: tsParser, - ecmaVersion: 5, - sourceType: 'commonjs', - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - - project: './tsconfig.json', + project: ['./tsconfig.json'], + tsconfigRootDir: import.meta.dirname, }, }, @@ -63,38 +29,55 @@ export default [ version: 'detect', }, }, - + }, + eslint.configs.recommended, + ...tseslint.configs.recommended, + ...tseslint.configs.stylisticTypeChecked, + ...tseslint.configs.strictTypeChecked, + eslintPluginReact.configs.flat.recommended, + { + plugins: { + 'react-hooks': eslintPluginReactHooks, + }, + rules: eslintPluginReactHooks.configs.recommended.rules, + }, + eslintPluginUnicorn.configs['flat/recommended'], + { + // in main config for TSX/JSX source files + plugins: { + 'react-refresh': eslintPluginReactRefresh, + }, + rules: {}, + }, + { rules: { - 'no-redeclare': 'off', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-base-to-string': 'off', - '@typescript-eslint/prefer-nullish-coalescing': 'off', - '@typescript-eslint/no-empty-function': 'off', - '@typescript-eslint/no-unused-vars': [ + 'no-empty': 'off', + 'no-console': [ 'warn', { - argsIgnorePattern: '^_', - ignoreRestSiblings: true, - caughtErrors: 'none', + allow: ['error', 'warn'], }, ], - '@typescript-eslint/no-floating-promises': 'error', - '@typescript-eslint/restrict-template-expressions': 'off', - '@typescript-eslint/restrict-plus-operands': 'off', - '@typescript-eslint/no-misused-promises': 'off', - '@typescript-eslint/require-await': 'off', - '@typescript-eslint/unbound-method': 'off', - '@typescript-eslint/no-unused-expressions': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-unsafe-argument': 'off', - '@typescript-eslint/no-unsafe-assignment': 'off', - '@typescript-eslint/no-unsafe-call': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - 'no-unexpected-multiline': 'error', + 'no-underscore-dangle': 'off', + curly: 'error', + semi: ['error', 'never'], + 'spaced-comment': [ + 'error', + 'always', + { + markers: ['/'], + }, + ], + + 'react-refresh/only-export-components': 'warn', + 'react/no-unescaped-entities': 'off', + 'react/prop-types': 'off', + + 'unicorn/prefer-structured-clone': 'off', 'unicorn/no-new-array': 'off', + 'unicorn/no-empty-file': 'off', 'unicorn/prefer-type-error': 'off', + 'unicorn/prefer-modern-math-apis': 'off', 'unicorn/prefer-node-protocol': 'off', 'unicorn/no-unreadable-array-destructuring': 'off', 'unicorn/no-abusive-eslint-disable': 'off', @@ -123,39 +106,45 @@ export default [ 'unicorn/relative-url-style': 'off', 'unicorn/prefer-math-trunc': 'off', 'unicorn/prefer-query-selector': 'off', + 'unicorn/no-negated-condition': 'off', 'unicorn/switch-case-braces': 'off', 'unicorn/prefer-switch': 'off', 'unicorn/better-regex': 'off', 'unicorn/no-for-loop': 'off', 'unicorn/escape-case': 'off', 'unicorn/prefer-number-properties': 'off', - curly: 'error', - 'no-use-before-define': 'off', - 'no-global-assign': 'warn', + 'unicorn/no-process-exit': 'off', + 'unicorn/prefer-at': 'off', + 'unicorn/prefer-string-replace-all': 'off', + 'unicorn/no-array-reduce': 'off', - 'no-console': [ + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-unnecessary-type-parameters': 'off', + '@typescript-eslint/no-base-to-string': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-argument': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/restrict-plus-operands': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/prefer-nullish-coalescing': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/require-await': 'off', + '@typescript-eslint/restrict-template-expressions': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/no-extraneous-class': 'off', + '@typescript-eslint/unbound-method': 'off', + '@typescript-eslint/no-dynamic-delete': 'off', + '@typescript-eslint/no-unused-vars': [ 'warn', { - allow: ['error', 'warn'], - }, - ], - - 'no-debugger': 'warn', - 'no-undef': 'error', - 'react/no-danger': 'warn', - 'react/prop-types': 'off', - 'react/destructuring-assignment': 'error', - 'react/no-unused-prop-types': 'error', - 'react/no-unused-state': 'error', - 'react/prefer-stateless-function': 'error', - - 'spaced-comment': [ - 'error', - 'always', - { - markers: ['/'], + argsIgnorePattern: '^_', + ignoreRestSiblings: true, + caughtErrors: 'none', }, ], }, }, -] +) diff --git a/lib/docgen/generateStateModelDocs.ts b/lib/docgen/generateStateModelDocs.ts index 030ed6c1..ee41d969 100644 --- a/lib/docgen/generateStateModelDocs.ts +++ b/lib/docgen/generateStateModelDocs.ts @@ -113,7 +113,7 @@ function generateStateModelDocs(files: string[]) { if (model) { const getterstr = `${getters.length ? `### ${model.name} - Getters` : ''}\n${getters .sort((a, b) => a.name.localeCompare(b.name)) - // eslint-disable-next-line @typescript-eslint/no-explicit-any + .map(({ name, docs, signature }: any) => { return `#### getter: ${name} @@ -129,7 +129,7 @@ ${signature || ''} const methodstr = `${methods.length ? `### ${model.name} - Methods` : ''}\n${methods .sort((a, b) => a.name.localeCompare(b.name)) - // eslint-disable-next-line @typescript-eslint/no-explicit-any + .map(({ name, docs, signature }: any) => { return `#### method: ${name} @@ -145,7 +145,7 @@ ${name}: ${signature || ''} const propertiesstr = `${properties.length ? `### ${model.name} - Properties` : ''}\n${properties .sort((a, b) => a.name.localeCompare(b.name)) - // eslint-disable-next-line @typescript-eslint/no-explicit-any + .map(({ name, docs, code, signature }: any) => { return `#### property: ${name} @@ -163,7 +163,7 @@ ${code} const actionstr = `${actions.length ? `### ${model.name} - Actions` : ''}\n${actions .sort((a, b) => a.name.localeCompare(b.name)) - // eslint-disable-next-line @typescript-eslint/no-explicit-any + .map(({ name, docs, signature }: any) => { return `#### action: ${name} diff --git a/lib/src/colorSchemes.ts b/lib/src/colorSchemes.ts index 32d97b1d..e5d0fac3 100644 --- a/lib/src/colorSchemes.ts +++ b/lib/src/colorSchemes.ts @@ -357,7 +357,7 @@ export function getClustalXColor( row: string, col: number, ) { - const l = model.columns[row][col] + const l = model.columns[row]![col]! const { W = 0, L = 0, @@ -504,7 +504,7 @@ export function getPercentIdentityColor( row: string, col: number, ) { - const l = model.columns[row][col] + const l = model.columns[row]![col]! const entries = Object.entries(stats) let ent = 0 let letter = '' diff --git a/lib/src/components/Loading.tsx b/lib/src/components/Loading.tsx index 14c59970..953e4de7 100644 --- a/lib/src/components/Loading.tsx +++ b/lib/src/components/Loading.tsx @@ -19,7 +19,13 @@ const Reset = observer(function ({ return (
-
diff --git a/lib/src/components/ResizeHandles.tsx b/lib/src/components/ResizeHandles.tsx index 8e57712f..581ce083 100644 --- a/lib/src/components/ResizeHandles.tsx +++ b/lib/src/components/ResizeHandles.tsx @@ -57,7 +57,9 @@ export const VerticalResizeHandle = observer(function ({ return (
setMouseDragging(true)} + onMouseDown={() => { + setMouseDragging(true) + }} style={{ cursor: 'ew-resize', height: '100%', @@ -123,7 +125,9 @@ export const HorizontalResizeHandle = observer(function ({ return (
setMouseDragging(true)} + onMouseDown={() => { + setMouseDragging(true) + }} style={{ cursor: 'ns-resize', width: '100%', diff --git a/lib/src/components/SequenceTextArea.tsx b/lib/src/components/SequenceTextArea.tsx index 3442d044..72735f99 100644 --- a/lib/src/components/SequenceTextArea.tsx +++ b/lib/src/components/SequenceTextArea.tsx @@ -25,7 +25,7 @@ export default function SequenceTextArea({ str }: { str: [string, string][] }) { const [showEmpty, setShowEmpty] = useState(false) const disp = str - .map(([s1, s2]) => [s1, showGaps ? s2 : s2.replaceAll('-', '')]) + .map(([s1, s2]) => [s1, showGaps ? s2 : s2.replaceAll('-', '')] as const) .filter(f => (showEmpty ? true : !!f[1])) .map(([s1, s2]) => `>${s1}\n${showGaps ? s2 : s2.replaceAll('-', '')}`) .join('\n') @@ -37,7 +37,9 @@ export default function SequenceTextArea({ str }: { str: [string, string][] }) { onClick={() => { copy(disp) setCopied(true) - setTimeout(() => setCopied(false), 500) + setTimeout(() => { + setCopied(false) + }, 500) }} > {copied ? 'Copied!' : 'Copy to clipboard'} @@ -45,12 +47,16 @@ export default function SequenceTextArea({ str }: { str: [string, string][] }) { setShowGaps(!showGaps)} + onChange={() => { + setShowGaps(!showGaps) + }} /> setShowEmpty(!showEmpty)} + onChange={() => { + setShowEmpty(!showEmpty) + }} /> () @@ -53,7 +53,9 @@ export const TrackLabel = observer(function ({ width: trackLabelHeight, height: trackLabelHeight, }} - onClick={event => setAnchorEl(event.currentTarget)} + onClick={event => { + setAnchorEl(event.currentTarget) + }} > @@ -62,7 +64,9 @@ export const TrackLabel = observer(function ({ anchorEl={anchorEl} transitionDuration={0} open - onClose={() => setAnchorEl(undefined)} + onClose={() => { + setAnchorEl(undefined) + }} > { height: Math.max(b - t, 20), zIndex: 100, }} - onMouseOver={() => setHovered(true)} - onMouseOut={() => setHovered(false)} + onMouseOver={() => { + setHovered(true) + }} + onMouseOut={() => { + setHovered(false) + }} onMouseDown={event => { setMouseDown({ clientY: event.clientY, diff --git a/lib/src/components/dialogs/AboutDialog.tsx b/lib/src/components/dialogs/AboutDialog.tsx index 00fe218c..e3fd3ee1 100644 --- a/lib/src/components/dialogs/AboutDialog.tsx +++ b/lib/src/components/dialogs/AboutDialog.tsx @@ -5,7 +5,13 @@ import { version } from '../../version' export default function AboutDialog({ onClose }: { onClose: () => void }) { return ( - onClose()} open title="About this plugin"> + { + onClose() + }} + open + title="About this plugin" + > MSAView {version} ( diff --git a/lib/src/components/dialogs/AddTrackDialog.tsx b/lib/src/components/dialogs/AddTrackDialog.tsx index 4390e631..d3a210f5 100644 --- a/lib/src/components/dialogs/AddTrackDialog.tsx +++ b/lib/src/components/dialogs/AddTrackDialog.tsx @@ -12,7 +12,7 @@ import type { FileLocation } from '@jbrowse/core/util/types' import { observer } from 'mobx-react' import type { MsaViewModel } from '../../model' -export default observer(function ({ +const AddTrackDialog = observer(function ({ model, onClose, open, @@ -26,7 +26,13 @@ export default observer(function ({ const [currentOption, setCurrentOption] = useState('') return ( - onClose()} open={open} title="Add track"> + { + onClose() + }} + open={open} + title="Add track" + > Open relevant per-alignment tracks e.g. protein domains @@ -62,7 +68,9 @@ export default observer(function ({ @@ -71,3 +79,5 @@ export default observer(function ({ ) }) + +export default AddTrackDialog diff --git a/lib/src/components/dialogs/DomainDialog.tsx b/lib/src/components/dialogs/DomainDialog.tsx index 24b0da82..0ce628ca 100644 --- a/lib/src/components/dialogs/DomainDialog.tsx +++ b/lib/src/components/dialogs/DomainDialog.tsx @@ -20,10 +20,17 @@ export default function LaunchDomainViewDialog({ handleClose()} + onClose={() => { + handleClose() + }} open > - setChoice(val)}> + { + setChoice(val) + }} + > diff --git a/lib/src/components/dialogs/ExportSVGDialog.tsx b/lib/src/components/dialogs/ExportSVGDialog.tsx index 126e08f0..4b0bc6e7 100644 --- a/lib/src/components/dialogs/ExportSVGDialog.tsx +++ b/lib/src/components/dialogs/ExportSVGDialog.tsx @@ -28,7 +28,13 @@ export default function ExportSVGDialog({ const [error, setError] = useState() const theme = useTheme() return ( - onClose()} open title="Export SVG"> + { + onClose() + }} + open + title="Export SVG" + > {error ? : null} Settings: @@ -36,14 +42,18 @@ export default function ExportSVGDialog({ label="Include minimap?" disabled={exportType === 'entire'} checked={includeMinimap} - onChange={() => setIncludeMinimap(!includeMinimap)} + onChange={() => { + setIncludeMinimap(!includeMinimap) + }} />
Export type setExportType(event.target.value)} + onChange={event => { + setExportType(event.target.value) + }} > { - try { - await model.exportSVG({ - theme, - includeMinimap: - exportType === 'entire' ? false : includeMinimap, - exportType, - }) - } catch (e) { - console.error(e) - setError(e) - } - onClose() + onClick={() => { + // eslint-disable-next-line @typescript-eslint/no-floating-promises + ;(async () => { + try { + await model.exportSVG({ + theme, + includeMinimap: + exportType === 'entire' ? false : includeMinimap, + exportType, + }) + } catch (e) { + console.error(e) + setError(e) + } + onClose() + })() }} > Submit - diff --git a/lib/src/components/dialogs/FeatureDialog.tsx b/lib/src/components/dialogs/FeatureDialog.tsx index e16f7522..98f9ac96 100644 --- a/lib/src/components/dialogs/FeatureDialog.tsx +++ b/lib/src/components/dialogs/FeatureDialog.tsx @@ -57,12 +57,12 @@ const Table = observer(function ({ model }: { model: MsaViewModel }) { + onChange={() => { model.setFilter( accession, !model.featureFilters.get(accession), ) - } + }} /> {accession} @@ -94,7 +94,9 @@ const FeatureTypeDialog = observer(function ({ }) { return ( onClose()} + onClose={() => { + onClose() + }} open title="Feature filters" maxWidth="xl" diff --git a/lib/src/components/dialogs/InterProScanDialog.tsx b/lib/src/components/dialogs/InterProScanDialog.tsx index 3b550ddd..d6ec0bea 100644 --- a/lib/src/components/dialogs/InterProScanDialog.tsx +++ b/lib/src/components/dialogs/InterProScanDialog.tsx @@ -151,7 +151,9 @@ const InterProScanDialog = observer(function ({ handleClose()} + onClose={() => { + handleClose() + }} open > @@ -159,7 +161,11 @@ const InterProScanDialog = observer(function ({ This will run InterProScan via the InterProScan API on all rows of the current MSA - {show ? ( @@ -169,18 +175,18 @@ const InterProScanDialog = observer(function ({ @@ -196,7 +202,7 @@ const InterProScanDialog = observer(function ({ type="checkbox" key={name} checked={checked} - onChange={() => + onChange={() => { setVals( vals.map(e => e.name === name @@ -204,7 +210,7 @@ const InterProScanDialog = observer(function ({ : e, ), ) - } + }} /> {name} @@ -220,7 +226,9 @@ const InterProScanDialog = observer(function ({ @@ -245,7 +253,9 @@ const InterProScanDialog = observer(function ({ .filter(f => !!f[1]) .map(row => `>${row[0]}\n${row[1]}`) .join('\n'), - onProgress: arg => model.setStatus(arg), + onProgress: arg => { + model.setStatus(arg) + }, model, }) } catch (e) { diff --git a/lib/src/components/dialogs/MetadataDialog.tsx b/lib/src/components/dialogs/MetadataDialog.tsx index 98fa582c..c987062b 100644 --- a/lib/src/components/dialogs/MetadataDialog.tsx +++ b/lib/src/components/dialogs/MetadataDialog.tsx @@ -21,7 +21,14 @@ const MetadataDialog = observer(function ({ const { header } = model return ( - onClose()} open title="Metadata" maxWidth="xl"> + { + onClose() + }} + open + title="Metadata" + maxWidth="xl" + > diff --git a/lib/src/components/dialogs/SettingsDialog.tsx b/lib/src/components/dialogs/SettingsDialog.tsx index a2b1fdb1..a6a4b664 100644 --- a/lib/src/components/dialogs/SettingsDialog.tsx +++ b/lib/src/components/dialogs/SettingsDialog.tsx @@ -23,6 +23,9 @@ const useStyles = makeStyles()(theme => ({ flex: { display: 'flex', }, + minw: { + width: '80em', + }, })) const SettingsContent = observer(function ({ model }: { model: MsaViewModel }) { @@ -56,39 +59,49 @@ const TreeSettings = observer(function TreeSettings({

Tree options

model.setShowBranchLen(!showBranchLen)} + onChange={() => { + model.setShowBranchLen(!showBranchLen) + }} label="Show branch length?" /> model.setDrawNodeBubbles(!drawNodeBubbles)} + onChange={() => { + model.setDrawNodeBubbles(!drawNodeBubbles) + }} label="Draw clickable bubbles on tree branches?" /> model.setDrawTree(!drawTree)} + onChange={() => { + model.setDrawTree(!drawTree) + }} label="Show tree?" /> model.setLabelsAlignRight(!labelsAlignRight)} + onChange={() => { + model.setLabelsAlignRight(!labelsAlignRight) + }} label="Tree labels align right?" /> model.setDrawLabels(!drawLabels)} + onChange={() => { + model.setDrawLabels(!drawLabels) + }} label="Draw labels" /> {noTree ? null : (
+ onChange={() => { model.setTreeWidthMatchesArea(!treeWidthMatchesArea) - } + }} label="Make tree width fit to tree area?" /> {treeWidthMatchesArea ? null : ( @@ -99,7 +112,9 @@ const TreeSettings = observer(function TreeSettings({ min={50} max={600} value={treeWidth} - onChange={(_, val) => model.setTreeWidth(val as number)} + onChange={(_, val) => { + model.setTreeWidth(val as number) + }} />
)} @@ -131,25 +146,46 @@ const MSASettings = observer(function MSASettings({

MSA options

model.setDrawMsaLetters(!drawMsaLetters)} + onChange={() => { + model.setDrawMsaLetters(!drawMsaLetters) + }} label="Draw letters" /> model.setBgColor(!bgColor)} + onChange={() => { + model.setBgColor(!bgColor) + }} label="Color background tiles of MSA?" /> model.setContrastLettering(!contrastLettering)} + onChange={() => { + model.setContrastLettering(!contrastLettering) + }} label="Use contrast lettering" /> model.setHideGaps(!hideGaps)} + onChange={() => { + model.setHideGaps(!hideGaps) + }} label={`Hide columns that are ${100 - allowedGappyness}% gaps`} /> - + {hideGaps ? ( +
+ Allowed gappyness ({100 - allowedGappyness}%) + { + model.setAllowedGappyness(val as number) + }} + /> +
+ ) : null}
Column width ({colWidth}px) model.setColWidth(val as number)} + onChange={(_, val) => { + model.setColWidth(val as number) + }} />
@@ -167,27 +205,19 @@ const MSASettings = observer(function MSASettings({ min={1} max={50} value={rowHeight} - onChange={(_, val) => model.setRowHeight(val as number)} + onChange={(_, val) => { + model.setRowHeight(val as number) + }} />
- {hideGaps ? ( -
- Allowed gappyness ({100 - allowedGappyness}%) - model.setAllowedGappyness(val as number)} - /> -
- ) : null} model.setColorSchemeName(event.target.value)} + onChange={event => { + model.setColorSchemeName(event.target.value) + }} > {Object.keys(colorSchemes).map(option => ( @@ -206,12 +236,26 @@ const SettingsDialog = observer(function ({ model: MsaViewModel onClose: () => void }) { + const { classes } = useStyles() return ( - onClose()} title="Settings" maxWidth="xl"> - + { + onClose() + }} + title="Settings" + maxWidth="xl" + > + - diff --git a/lib/src/components/dialogs/TracklistDialog.tsx b/lib/src/components/dialogs/TracklistDialog.tsx index 926add09..c95cfc08 100644 --- a/lib/src/components/dialogs/TracklistDialog.tsx +++ b/lib/src/components/dialogs/TracklistDialog.tsx @@ -14,7 +14,7 @@ import { observer } from 'mobx-react' // locals import type { MsaViewModel } from '../../model' -export default observer(function ({ +const TracklistDialog = observer(function ({ model, onClose, }: { @@ -24,7 +24,13 @@ export default observer(function ({ const { tracks } = model return ( - onClose()} open title="Add track"> + { + onClose() + }} + open + title="Add track" + > Open relevant per-alignment tracks e.g. protein domains @@ -49,7 +55,13 @@ export default observer(function ({ })} - @@ -57,3 +69,5 @@ export default observer(function ({ ) }) + +export default TracklistDialog diff --git a/lib/src/components/dialogs/UserProvidedDomainsDialog.tsx b/lib/src/components/dialogs/UserProvidedDomainsDialog.tsx index da10d037..5cdb9ae3 100644 --- a/lib/src/components/dialogs/UserProvidedDomainsDialog.tsx +++ b/lib/src/components/dialogs/UserProvidedDomainsDialog.tsx @@ -34,7 +34,9 @@ const UserProvidedDomainsDialog = observer(function ({ handleClose()} + onClose={() => { + handleClose() + }} open > @@ -48,7 +50,9 @@ const UserProvidedDomainsDialog = observer(function ({ setChoice(event.target.value)} + onChange={event => { + setChoice(event.target.value) + }} > } label="URL" /> setInterProURL(event.target.value)} + onChange={event => { + setInterProURL(event.target.value) + }} />
) : null} @@ -81,7 +87,7 @@ const UserProvidedDomainsDialog = observer(function ({ type="file" hidden onChange={({ target }) => { - const file = target?.files?.[0] + const file = target.files?.[0] if (file) { setFile(file) } @@ -106,7 +112,7 @@ const UserProvidedDomainsDialog = observer(function ({ : await jsonfetch(interProURL) model.setInterProAnnotations( - Object.fromEntries(ret.results.map(r => [r.xref[0].id, r])), + Object.fromEntries(ret.results.map(r => [r.xref[0]!.id, r])), ) model.setShowDomains(true) getSession(model).notify( diff --git a/lib/src/components/header/Header.tsx b/lib/src/components/header/Header.tsx index dd754602..e1f5b421 100644 --- a/lib/src/components/header/Header.tsx +++ b/lib/src/components/header/Header.tsx @@ -32,7 +32,9 @@ const Header = observer(function ({ model }: { model: MsaViewModel }) { model.queueDialog(onClose => [AboutDialog, { onClose }])} + onClick={() => { + model.queueDialog(onClose => [AboutDialog, { onClose }]) + }} > diff --git a/lib/src/components/header/HeaderMenu.tsx b/lib/src/components/header/HeaderMenu.tsx index c8c4dd0c..cf8d071e 100644 --- a/lib/src/components/header/HeaderMenu.tsx +++ b/lib/src/components/header/HeaderMenu.tsx @@ -24,24 +24,29 @@ const HeaderMenu = observer(function ({ model }: { model: MsaViewModel }) { { label: 'Return to import form', icon: FolderOpen, - onClick: () => model.reset(), + onClick: () => { + model.reset() + }, }, { label: 'Settings', - onClick: () => - model.queueDialog(onClose => [SettingsDialog, { model, onClose }]), + onClick: () => { + model.queueDialog(onClose => [SettingsDialog, { model, onClose }]) + }, icon: Settings, }, { label: 'Metadata', - onClick: () => - model.queueDialog(onClose => [MetadataDialog, { model, onClose }]), + onClick: () => { + model.queueDialog(onClose => [MetadataDialog, { model, onClose }]) + }, icon: Assignment, }, { label: 'Extra tracks', - onClick: () => - model.queueDialog(onClose => [TracklistDialog, { model, onClose }]), + onClick: () => { + model.queueDialog(onClose => [TracklistDialog, { model, onClose }]) + }, icon: List, }, ]} diff --git a/lib/src/components/header/HeaderMenuExtra.tsx b/lib/src/components/header/HeaderMenuExtra.tsx index 35c6aa82..ca60170c 100644 --- a/lib/src/components/header/HeaderMenuExtra.tsx +++ b/lib/src/components/header/HeaderMenuExtra.tsx @@ -36,32 +36,38 @@ const HeaderMenuExtra = observer(({ model }: { model: MsaViewModel }) => { { label: 'Return to import form', icon: FolderOpen, - onClick: () => model.reset(), + onClick: () => { + model.reset() + }, }, { label: 'Settings', - onClick: () => - model.queueDialog(onClose => [SettingsDialog, { model, onClose }]), + onClick: () => { + model.queueDialog(onClose => [SettingsDialog, { model, onClose }]) + }, icon: Settings, }, { label: 'Metadata', - onClick: () => - model.queueDialog(onClose => [MetadataDialog, { model, onClose }]), + onClick: () => { + model.queueDialog(onClose => [MetadataDialog, { model, onClose }]) + }, icon: Assignment, }, { label: 'Extra tracks', - onClick: () => - model.queueDialog(onClose => [TracklistDialog, { model, onClose }]), + onClick: () => { + model.queueDialog(onClose => [TracklistDialog, { model, onClose }]) + }, icon: List, }, { label: 'Export SVG', icon: PhotoCamera, - onClick: () => - model.queueDialog(onClose => [ExportSVGDialog, { onClose, model }]), + onClick: () => { + model.queueDialog(onClose => [ExportSVGDialog, { onClose, model }]) + }, }, { label: 'Features/protein domains', @@ -70,20 +76,22 @@ const HeaderMenuExtra = observer(({ model }: { model: MsaViewModel }) => { { label: 'Open domains...', icon: FolderOpen, - onClick: () => + onClick: () => { model.queueDialog(handleClose => [ UserProvidedDomainsDialog, { handleClose, model }, - ]), + ]) + }, }, { label: 'Query InterProScan for domains...', icon: Search, - onClick: () => + onClick: () => { model.queueDialog(handleClose => [ InterProScanDialog, { handleClose, model }, - ]), + ]) + }, }, { label: `Show domains${noDomains ? ' (no domains loaded)' : ''}`, @@ -91,7 +99,9 @@ const HeaderMenuExtra = observer(({ model }: { model: MsaViewModel }) => { icon: Visibility, checked: actuallyShowDomains ? showDomains : false, type: 'checkbox', - onClick: () => model.setShowDomains(!showDomains), + onClick: () => { + model.setShowDomains(!showDomains) + }, }, { label: `Use sub-row layout${noDomains ? ' (no domains loaded)' : ''}`, @@ -99,7 +109,9 @@ const HeaderMenuExtra = observer(({ model }: { model: MsaViewModel }) => { checked: actuallyShowDomains ? subFeatureRows : false, icon: Sort, type: 'checkbox', - onClick: () => model.setSubFeatureRows(!subFeatureRows), + onClick: () => { + model.setSubFeatureRows(!subFeatureRows) + }, }, { label: `Filter domains${noDomains ? ' (no domains loaded)' : ''}`, @@ -114,7 +126,7 @@ const HeaderMenuExtra = observer(({ model }: { model: MsaViewModel }) => { }, ], }, - ...(model.extraViewMenuItems?.() || []), + ...model.extraViewMenuItems(), ]} > diff --git a/lib/src/components/header/ZoomControls.tsx b/lib/src/components/header/ZoomControls.tsx index c9cc5bfd..bd412800 100644 --- a/lib/src/components/header/ZoomControls.tsx +++ b/lib/src/components/header/ZoomControls.tsx @@ -19,30 +19,46 @@ const ZoomControls = observer(function ZoomControls({ }) { return ( <> - model.zoomIn()}> + { + model.zoomIn() + }} + > - model.zoomOut()}> + { + model.zoomOut() + }} + > model.zoomInHorizontal(), + onClick: () => { + model.zoomInHorizontal() + }, }, { label: 'Zoom in vertical', - onClick: () => model.zoomInVertical(), + onClick: () => { + model.zoomInVertical() + }, }, { label: 'Zoom out horizontal', - onClick: () => model.zoomOutHorizontal(), + onClick: () => { + model.zoomOutHorizontal() + }, }, { label: 'Zoom out vertical', - onClick: () => model.zoomOutVertical(), + onClick: () => { + model.zoomOutVertical() + }, }, { label: 'Reset zoom to default', diff --git a/lib/src/components/import/ImportForm.tsx b/lib/src/components/import/ImportForm.tsx index 09793bec..9d8a53b5 100644 --- a/lib/src/components/import/ImportForm.tsx +++ b/lib/src/components/import/ImportForm.tsx @@ -9,7 +9,7 @@ import type { MsaViewModel } from '../../model' import { load } from './util' import ImportFormExamples from './ImportFormExamples' -export default observer(function ({ model }: { model: MsaViewModel }) { +const ImportForm = observer(function ({ model }: { model: MsaViewModel }) { const [msaFile, setMsaFile] = useState() const [treeFile, setTreeFile] = useState() const { error } = model @@ -44,7 +44,17 @@ export default observer(function ({ model }: { model: MsaViewModel }) {