@rivm-loculus-tools/common-frontend 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/LoculusService.js +1055 -0
- package/dist/_chunks/LoculusService.js.map +1 -0
- package/dist/_chunks/decorateParam.js +7 -0
- package/dist/_chunks/keycloak-dpop.js +241 -0
- package/dist/_chunks/keycloak-dpop.js.map +1 -0
- package/dist/classes/services/EmotionCacheService/index.js +27 -1
- package/dist/classes/services/EmotionCacheService/index.js.map +1 -0
- package/dist/classes/services/LoculusService/LoculusService.d.ts +26 -0
- package/dist/classes/services/LoculusService/index.d.ts +1 -0
- package/dist/classes/services/LoculusService/index.js +1 -0
- package/dist/classes/services/UrlService/UrlService.d.ts +18 -0
- package/dist/classes/services/UrlService/index.d.ts +1 -0
- package/dist/classes/services/UrlService/index.js +45 -0
- package/dist/classes/services/UrlService/index.js.map +1 -0
- package/dist/components/app/App/App.d.ts +4 -2
- package/dist/components/app/App/index.js +35 -34
- package/dist/components/app/App/index.js.map +1 -1
- package/dist/components/authentication/AuthenticationLoader/AuthenticationLoader.d.ts +3 -0
- package/dist/components/authentication/AuthenticationLoader/index.d.ts +1 -0
- package/dist/components/authentication/AuthenticationLoader/index.js +31 -0
- package/dist/components/authentication/AuthenticationLoader/index.js.map +1 -0
- package/dist/components/fasta/FastaDataImportDialog/FastaDataImportDialog.d.ts +1 -0
- package/dist/components/fasta/FastaDataImportDialog/index.js +77 -77
- package/dist/components/fasta/FastaDataImportDialog/index.js.map +1 -1
- package/dist/components/fasta/FastaDataImportDialogLoader/FastaDataImportDialogLoader.d.ts +2 -1
- package/dist/components/fasta/FastaDataImportDialogLoader/index.js +66 -52
- package/dist/components/fasta/FastaDataImportDialogLoader/index.js.map +1 -1
- package/dist/components/fasta/FastaPageHeaderCenterComponent/FastaPageHeaderCenterComponent.d.ts +3 -4
- package/dist/components/fasta/FastaPageHeaderCenterComponent/index.js +18 -22
- package/dist/components/fasta/FastaPageHeaderCenterComponent/index.js.map +1 -1
- package/dist/components/fasta/FastaPageHeaderResetButton/index.js +27 -24
- package/dist/components/fasta/FastaPageHeaderResetButton/index.js.map +1 -1
- package/dist/components/loculus/UploadToLoculusModal/UploadToLoculusModal.d.ts +11 -0
- package/dist/components/loculus/UploadToLoculusModal/index.d.ts +1 -0
- package/dist/components/loculus/UploadToLoculusModal/index.js +159 -0
- package/dist/components/loculus/UploadToLoculusModal/index.js.map +1 -0
- package/dist/components/ui/ButtonWithMenu/ButtonWithMenu.d.ts +1 -0
- package/dist/components/ui/ButtonWithMenu/index.js +5 -4
- package/dist/components/ui/ButtonWithMenu/index.js.map +1 -1
- package/dist/components/ui/PageCommon/index.js +19 -6
- package/dist/components/ui/PageCommon/index.js.map +1 -1
- package/dist/components/ui/PageCommonDrawer/index.js +18 -10
- package/dist/components/ui/PageCommonDrawer/index.js.map +1 -1
- package/dist/components/ui/PageCommonHeader/index.js +7 -6
- package/dist/components/ui/PageCommonHeader/index.js.map +1 -1
- package/dist/components/ui/SearchBar/index.js +1 -0
- package/dist/components/ui/SearchBar/index.js.map +1 -1
- package/dist/context/pageCommonContext/PageCommonContext.d.ts +1 -0
- package/dist/context/pageCommonContext/index.js +7 -5
- package/dist/context/pageCommonContext/index.js.map +1 -1
- package/dist/models/url.models.d.ts +9 -0
- package/dist/models/url.models.js +0 -0
- package/dist/utils/MetaDataUtil/MetaDataUtil.d.ts +2 -1
- package/dist/utils/MetaDataUtil/index.js +5 -2
- package/dist/utils/MetaDataUtil/index.js.map +1 -1
- package/dist/utils/UrlUtil/UrlUtil.d.ts +1 -1
- package/dist/utils/UrlUtil/index.js +5 -6
- package/dist/utils/UrlUtil/index.js.map +1 -1
- package/package.json +23 -2
- package/dist/_chunks/EmotionCacheService.js +0 -32
- package/dist/_chunks/EmotionCacheService.js.map +0 -1
- package/dist/_chunks/react.js +0 -10
- package/dist/_chunks/react.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["_Fragment","SearchIcon","KeyboardArrowUpIcon","KeyboardArrowDownIcon","CloseIcon","SearchBarFilledInput","styled","FilledInput","theme","SearchBar","useThemeProps","inProps","useTheme","searchBarTheme","useMemo","inputRef","useRef","arrowUpButtonRef","arrowDownButtonRef","onTextInputChange","useCallback","nextSearchText","event","onSearchQueryChange","onClearButtonClick","onTextInputKeyDown","numResults","onHighlightedIndexChange","onUpArrowButtonClick","newHighlightedIndex","highlightedIndex","onDownArrowButtonClick","onGenericKeyDown","nextFocusableElement","previousFocusableElement","searchResultStatusText","searchQuery","allowNavigation","truncated","InputAdornment","Box","Fragment","IconButton"],"sources":["../../../../src/components/ui/SearchBar/SearchBar.tsx"],"sourcesContent":["import type {\n ChangeEvent,\n KeyboardEvent,\n} from 'react';\nimport {\n useCallback,\n useMemo,\n useRef,\n} from 'react';\nimport {\n Box,\n FilledInput,\n IconButton,\n InputAdornment,\n styled,\n useTheme,\n useThemeProps,\n} from '@mui/material';\nimport type {\n ComponentsOverrides,\n Theme,\n} from '@mui/material/styles';\nimport SearchIcon from '@mui/icons-material/Search';\nimport KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';\nimport KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';\nimport CloseIcon from '@mui/icons-material/Close';\n\nexport type SearchBarProps = {\n allowNavigation?: boolean;\n highlightedIndex?: null | number;\n numResults?: number;\n onHighlightedIndexChange?: (index: number) => void;\n onSearchQueryChange: (query: string) => void;\n searchQuery: string;\n truncated?: boolean;\n};\n\nexport interface SearchBarTheme {\n buttonColor: string;\n styleOverrides?: ComponentsOverrides<Theme>['MuiFilledInput'];\n}\n\nconst SearchBarFilledInput = styled(FilledInput, {\n name: 'RivmLoculusToolsCommonFrontendSearchBar',\n slot: 'Root',\n})(({ theme }) => ({\n backgroundColor: theme.palette.background.paper,\n height: theme.spacing(4),\n}));\n\nexport const SearchBar = (inProps: SearchBarProps) => {\n const {\n allowNavigation,\n highlightedIndex,\n numResults = 0,\n onHighlightedIndexChange,\n onSearchQueryChange,\n searchQuery,\n truncated,\n } = useThemeProps({ name: 'RivmLoculusToolsCommonFrontendSearchBar', props: inProps });\n const theme = useTheme();\n const searchBarTheme = useMemo<SearchBarTheme>(() => ({\n ...{\n buttonColor: theme.palette.primary.main,\n } satisfies SearchBarTheme,\n ...(theme.components?.RivmLoculusToolsCommonFrontendSearchBar ?? {}),\n }), [theme]);\n\n\n const inputRef = useRef<HTMLInputElement>(null);\n const arrowUpButtonRef = useRef<HTMLButtonElement>(null);\n const arrowDownButtonRef = useRef<HTMLButtonElement>(null);\n\n const onTextInputChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {\n const nextSearchText = event.target.value;\n onSearchQueryChange(nextSearchText);\n }, [onSearchQueryChange]);\n\n const onClearButtonClick = useCallback(() => {\n onSearchQueryChange('');\n inputRef.current?.focus();\n }, [onSearchQueryChange]);\n\n const onTextInputKeyDown = useCallback((event: KeyboardEvent<HTMLInputElement>) => {\n // on enter press, highlight the first result and focus the down arrow button\n if (event.key === 'Enter') {\n if (!numResults) {\n return;\n }\n event.preventDefault();\n onHighlightedIndexChange?.(0);\n arrowDownButtonRef.current?.focus();\n }\n }, [numResults, onHighlightedIndexChange]);\n\n const onUpArrowButtonClick = useCallback(() => {\n // goto previous result\n if (!numResults) {\n return;\n }\n const newHighlightedIndex = highlightedIndex === null || highlightedIndex === undefined || Number.isNaN(highlightedIndex) || highlightedIndex <= 0 ? numResults - 1 : highlightedIndex - 1;\n\n onHighlightedIndexChange?.(newHighlightedIndex);\n }, [numResults, highlightedIndex, onHighlightedIndexChange]);\n\n const onDownArrowButtonClick = useCallback(() => {\n // goto next result\n if (!numResults) {\n return;\n }\n const newHighlightedIndex = highlightedIndex === null || highlightedIndex === undefined || Number.isNaN(highlightedIndex) || highlightedIndex >= numResults - 1 ? 0 : highlightedIndex + 1;\n onHighlightedIndexChange?.(newHighlightedIndex);\n }, [numResults, highlightedIndex, onHighlightedIndexChange]);\n\n const onGenericKeyDown = useCallback((event: KeyboardEvent<HTMLButtonElement>) => {\n // arrow right, focus the next focusable element\n if (event.key === 'ArrowRight') {\n event.preventDefault();\n const nextFocusableElement = event.currentTarget.nextElementSibling as HTMLElement;\n if (nextFocusableElement) {\n nextFocusableElement.focus();\n }\n }\n // arrow left, focus the previous focusable element\n if (event.key === 'ArrowLeft') {\n event.preventDefault();\n if (event.target === arrowUpButtonRef.current) {\n inputRef.current?.focus();\n return;\n }\n const previousFocusableElement = event.currentTarget.previousElementSibling as HTMLElement;\n if (previousFocusableElement) {\n previousFocusableElement.focus();\n }\n }\n }, []);\n\n const searchResultStatusText = useMemo(() => {\n if (!searchQuery) {\n return '';\n }\n if (searchQuery && (Number.isNaN(numResults) || numResults <= 0)) {\n return 'No results';\n } else if (highlightedIndex === null || highlightedIndex === undefined || Number.isNaN(highlightedIndex) || highlightedIndex < 0 || !allowNavigation) {\n return `${numResults}${truncated ? '+' : ''} results`;\n } else if (allowNavigation) {\n return `${highlightedIndex + 1}/${numResults}${truncated ? '+' : ''} results`;\n }\n return '';\n }, [searchQuery, numResults, highlightedIndex, allowNavigation, truncated]);\n\n\n return (\n <SearchBarFilledInput\n endAdornment={(\n <InputAdornment position={'end'}>\n {searchResultStatusText && (\n <Box\n aria-atomic={'true'}\n aria-live={'polite'}\n role={'status'}\n sx={{\n fontSize: '1rem',\n }}\n >\n {searchResultStatusText}\n </Box>\n )}\n {numResults > 0 && allowNavigation && (\n <>\n <IconButton\n aria-label={'Goto previous search result'}\n disabled={!numResults}\n edge={'end'}\n onClick={onUpArrowButtonClick}\n onKeyDown={onGenericKeyDown}\n ref={arrowUpButtonRef}\n sx={{ color: searchBarTheme.buttonColor }}\n >\n <KeyboardArrowUpIcon />\n </IconButton>\n <IconButton\n aria-label={'Goto next search result'}\n disabled={!numResults}\n edge={'end'}\n onClick={onDownArrowButtonClick}\n onKeyDown={onGenericKeyDown}\n ref={arrowDownButtonRef}\n sx={{ color: searchBarTheme.buttonColor }}\n >\n <KeyboardArrowDownIcon />\n </IconButton>\n </>\n )}\n {searchQuery && (\n <IconButton\n aria-label={'Clear'}\n edge={'end'}\n onClick={onClearButtonClick}\n onKeyDown={onGenericKeyDown}\n >\n <CloseIcon />\n </IconButton>\n )}\n </InputAdornment>\n )}\n fullWidth\n inputRef={inputRef}\n name={'Search'}\n onChange={onTextInputChange}\n onKeyDown={onTextInputKeyDown}\n placeholder={'Search'}\n size={'small'}\n slotProps={{\n input: {\n 'aria-label': 'Search',\n sx: {\n '&::-webkit-search-cancel-button': {\n display: 'none',\n },\n fontSize: '1rem',\n height: theme.spacing(4),\n outline: 'none !important',\n paddingBottom: '0 !important',\n paddingTop: '0 !important',\n },\n },\n }}\n startAdornment={(\n <InputAdornment position={'start'}>\n <SearchIcon fontSize={'small'} />\n </InputAdornment>\n )}\n type={'search'}\n value={searchQuery}\n />\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["_Fragment","SearchIcon","KeyboardArrowUpIcon","KeyboardArrowDownIcon","CloseIcon","SearchBarFilledInput","styled","FilledInput","theme","SearchBar","useThemeProps","inProps","useTheme","searchBarTheme","useMemo","inputRef","useRef","arrowUpButtonRef","arrowDownButtonRef","onTextInputChange","useCallback","nextSearchText","event","onSearchQueryChange","onClearButtonClick","onTextInputKeyDown","numResults","onHighlightedIndexChange","onUpArrowButtonClick","newHighlightedIndex","highlightedIndex","onDownArrowButtonClick","onGenericKeyDown","nextFocusableElement","previousFocusableElement","searchResultStatusText","searchQuery","allowNavigation","truncated","InputAdornment","Box","Fragment","IconButton"],"sources":["../../../../src/components/ui/SearchBar/SearchBar.tsx"],"sourcesContent":["import type {\n ChangeEvent,\n KeyboardEvent,\n} from 'react';\nimport {\n useCallback,\n useMemo,\n useRef,\n} from 'react';\nimport {\n Box,\n FilledInput,\n IconButton,\n InputAdornment,\n styled,\n useTheme,\n useThemeProps,\n} from '@mui/material';\nimport type {\n ComponentsOverrides,\n Theme,\n} from '@mui/material/styles';\nimport SearchIcon from '@mui/icons-material/Search';\nimport KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';\nimport KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';\nimport CloseIcon from '@mui/icons-material/Close';\n\nexport type SearchBarProps = {\n allowNavigation?: boolean;\n highlightedIndex?: null | number;\n numResults?: number;\n onHighlightedIndexChange?: (index: number) => void;\n onSearchQueryChange: (query: string) => void;\n searchQuery: string;\n truncated?: boolean;\n};\n\nexport interface SearchBarTheme {\n buttonColor: string;\n styleOverrides?: ComponentsOverrides<Theme>['MuiFilledInput'];\n}\n\nconst SearchBarFilledInput = styled(FilledInput, {\n name: 'RivmLoculusToolsCommonFrontendSearchBar',\n slot: 'Root',\n})(({ theme }) => ({\n backgroundColor: theme.palette.background.paper,\n height: theme.spacing(4),\n}));\n\nexport const SearchBar = (inProps: SearchBarProps) => {\n const {\n allowNavigation,\n highlightedIndex,\n numResults = 0,\n onHighlightedIndexChange,\n onSearchQueryChange,\n searchQuery,\n truncated,\n } = useThemeProps({ name: 'RivmLoculusToolsCommonFrontendSearchBar', props: inProps });\n const theme = useTheme();\n const searchBarTheme = useMemo<SearchBarTheme>(() => ({\n ...{\n buttonColor: theme.palette.primary.main,\n } satisfies SearchBarTheme,\n ...(theme.components?.RivmLoculusToolsCommonFrontendSearchBar ?? {}),\n }), [theme]);\n\n\n const inputRef = useRef<HTMLInputElement>(null);\n const arrowUpButtonRef = useRef<HTMLButtonElement>(null);\n const arrowDownButtonRef = useRef<HTMLButtonElement>(null);\n\n const onTextInputChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {\n const nextSearchText = event.target.value;\n onSearchQueryChange(nextSearchText);\n }, [onSearchQueryChange]);\n\n const onClearButtonClick = useCallback(() => {\n onSearchQueryChange('');\n inputRef.current?.focus();\n }, [onSearchQueryChange]);\n\n const onTextInputKeyDown = useCallback((event: KeyboardEvent<HTMLInputElement>) => {\n // on enter press, highlight the first result and focus the down arrow button\n if (event.key === 'Enter') {\n if (!numResults) {\n return;\n }\n event.preventDefault();\n onHighlightedIndexChange?.(0);\n arrowDownButtonRef.current?.focus();\n }\n }, [numResults, onHighlightedIndexChange]);\n\n const onUpArrowButtonClick = useCallback(() => {\n // goto previous result\n if (!numResults) {\n return;\n }\n const newHighlightedIndex = highlightedIndex === null || highlightedIndex === undefined || Number.isNaN(highlightedIndex) || highlightedIndex <= 0 ? numResults - 1 : highlightedIndex - 1;\n\n onHighlightedIndexChange?.(newHighlightedIndex);\n }, [numResults, highlightedIndex, onHighlightedIndexChange]);\n\n const onDownArrowButtonClick = useCallback(() => {\n // goto next result\n if (!numResults) {\n return;\n }\n const newHighlightedIndex = highlightedIndex === null || highlightedIndex === undefined || Number.isNaN(highlightedIndex) || highlightedIndex >= numResults - 1 ? 0 : highlightedIndex + 1;\n onHighlightedIndexChange?.(newHighlightedIndex);\n }, [numResults, highlightedIndex, onHighlightedIndexChange]);\n\n const onGenericKeyDown = useCallback((event: KeyboardEvent<HTMLButtonElement>) => {\n // arrow right, focus the next focusable element\n if (event.key === 'ArrowRight') {\n event.preventDefault();\n const nextFocusableElement = event.currentTarget.nextElementSibling as HTMLElement;\n if (nextFocusableElement) {\n nextFocusableElement.focus();\n }\n }\n // arrow left, focus the previous focusable element\n if (event.key === 'ArrowLeft') {\n event.preventDefault();\n if (event.target === arrowUpButtonRef.current) {\n inputRef.current?.focus();\n return;\n }\n const previousFocusableElement = event.currentTarget.previousElementSibling as HTMLElement;\n if (previousFocusableElement) {\n previousFocusableElement.focus();\n }\n }\n }, []);\n\n const searchResultStatusText = useMemo(() => {\n if (!searchQuery) {\n return '';\n }\n if (searchQuery && (Number.isNaN(numResults) || numResults <= 0)) {\n return 'No results';\n } else if (highlightedIndex === null || highlightedIndex === undefined || Number.isNaN(highlightedIndex) || highlightedIndex < 0 || !allowNavigation) {\n return `${numResults}${truncated ? '+' : ''} results`;\n } else if (allowNavigation) {\n return `${highlightedIndex + 1}/${numResults}${truncated ? '+' : ''} results`;\n }\n return '';\n }, [searchQuery, numResults, highlightedIndex, allowNavigation, truncated]);\n\n\n return (\n <SearchBarFilledInput\n autoComplete={'off'}\n endAdornment={(\n <InputAdornment position={'end'}>\n {searchResultStatusText && (\n <Box\n aria-atomic={'true'}\n aria-live={'polite'}\n role={'status'}\n sx={{\n fontSize: '1rem',\n }}\n >\n {searchResultStatusText}\n </Box>\n )}\n {numResults > 0 && allowNavigation && (\n <>\n <IconButton\n aria-label={'Goto previous search result'}\n disabled={!numResults}\n edge={'end'}\n onClick={onUpArrowButtonClick}\n onKeyDown={onGenericKeyDown}\n ref={arrowUpButtonRef}\n sx={{ color: searchBarTheme.buttonColor }}\n >\n <KeyboardArrowUpIcon />\n </IconButton>\n <IconButton\n aria-label={'Goto next search result'}\n disabled={!numResults}\n edge={'end'}\n onClick={onDownArrowButtonClick}\n onKeyDown={onGenericKeyDown}\n ref={arrowDownButtonRef}\n sx={{ color: searchBarTheme.buttonColor }}\n >\n <KeyboardArrowDownIcon />\n </IconButton>\n </>\n )}\n {searchQuery && (\n <IconButton\n aria-label={'Clear'}\n edge={'end'}\n onClick={onClearButtonClick}\n onKeyDown={onGenericKeyDown}\n >\n <CloseIcon />\n </IconButton>\n )}\n </InputAdornment>\n )}\n fullWidth\n inputRef={inputRef}\n name={'Search'}\n onChange={onTextInputChange}\n onKeyDown={onTextInputKeyDown}\n placeholder={'Search'}\n size={'small'}\n slotProps={{\n input: {\n 'aria-label': 'Search',\n sx: {\n '&::-webkit-search-cancel-button': {\n display: 'none',\n },\n fontSize: '1rem',\n height: theme.spacing(4),\n outline: 'none !important',\n paddingBottom: '0 !important',\n paddingTop: '0 !important',\n },\n },\n }}\n startAdornment={(\n <InputAdornment position={'start'}>\n <SearchIcon fontSize={'small'} />\n </InputAdornment>\n )}\n type={'search'}\n value={searchQuery}\n />\n );\n};\n"],"mappings":"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"}
|
|
@@ -8,6 +8,7 @@ export type PageCommonConfig = {
|
|
|
8
8
|
readonly beforeAppBarRightComponent?: ReactNode;
|
|
9
9
|
};
|
|
10
10
|
export type PageCommonContextData = {
|
|
11
|
+
readonly drawerButtonId: string;
|
|
11
12
|
readonly drawerContainerId: string;
|
|
12
13
|
readonly isDrawerOpen: boolean;
|
|
13
14
|
readonly isNoInteraction: boolean;
|
|
@@ -2,21 +2,23 @@ import { jsx as e } from "react/jsx-runtime";
|
|
|
2
2
|
import { createContext as t, useId as n, useMemo as r, useState as i } from "react";
|
|
3
3
|
// src/context/pageCommonContext/PageCommonContext.ts
|
|
4
4
|
var a = t({}), o = ({ children: t, ...o }) => {
|
|
5
|
-
let [s, c] = i(!1), [l, u] = i(!1), d = n(), f = r(() => ({
|
|
5
|
+
let [s, c] = i(!1), [l, u] = i(!1), d = n(), f = n(), p = r(() => ({
|
|
6
6
|
...o,
|
|
7
|
+
drawerButtonId: f,
|
|
7
8
|
drawerContainerId: d,
|
|
8
9
|
isDrawerOpen: s,
|
|
9
10
|
isNoInteraction: l,
|
|
10
11
|
setIsDrawerOpen: c,
|
|
11
12
|
setIsNoInteraction: u
|
|
12
13
|
}), [
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
o,
|
|
15
|
+
f,
|
|
15
16
|
d,
|
|
16
|
-
|
|
17
|
+
s,
|
|
18
|
+
l
|
|
17
19
|
]);
|
|
18
20
|
return /* @__PURE__ */ e(a, {
|
|
19
|
-
value:
|
|
21
|
+
value: p,
|
|
20
22
|
children: t
|
|
21
23
|
});
|
|
22
24
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["PageCommonContext","createContext","PageCommonContextProvider","props","isDrawerOpen","setIsDrawerOpen","useState","isNoInteraction","setIsNoInteraction","drawerContainerId","useId","contextValue","useMemo"],"sources":["../../../src/context/pageCommonContext/PageCommonContext.ts","../../../src/context/pageCommonContext/PageCommonContextProvider.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { createContext } from 'react';\n\nexport type PageCommonConfig = {\n readonly afterAppBarComponent?: ReactNode;\n readonly afterAppBarRightComponent?: ReactNode;\n readonly appBarHeight?: number;\n readonly appBarIcon?: ReactNode;\n readonly beforeAppBarComponent?: ReactNode;\n readonly beforeAppBarRightComponent?: ReactNode;\n};\n\nexport type PageCommonContextData = {\n readonly drawerContainerId: string;\n readonly isDrawerOpen: boolean;\n readonly isNoInteraction: boolean;\n readonly setIsDrawerOpen: (open: boolean) => void;\n readonly setIsNoInteraction: (open: boolean) => void;\n} & PageCommonConfig;\n\nexport const PageCommonContext = createContext<PageCommonContextData>({} as PageCommonContextData);\n","import {\n useId,\n useMemo,\n useState,\n} from 'react';\nimport type { PropsWithChildren } from 'react';\n\nimport type {\n PageCommonConfig,\n PageCommonContextData,\n} from './PageCommonContext';\nimport { PageCommonContext } from './PageCommonContext';\n\nexport type PageCommonContextProviderProps = PropsWithChildren<PageCommonConfig>;\n\nexport const PageCommonContextProvider = ({ children, ...props }: PageCommonContextProviderProps) => {\n const [isDrawerOpen, setIsDrawerOpen] = useState(false);\n const [isNoInteraction, setIsNoInteraction] = useState(false);\n const drawerContainerId = useId();\n\n const contextValue = useMemo<PageCommonContextData>(() => {\n return {\n ...props,\n drawerContainerId,\n isDrawerOpen,\n isNoInteraction,\n setIsDrawerOpen,\n setIsNoInteraction,\n };\n }, [
|
|
1
|
+
{"version":3,"file":"index.js","names":["PageCommonContext","createContext","PageCommonContextProvider","props","isDrawerOpen","setIsDrawerOpen","useState","isNoInteraction","setIsNoInteraction","drawerContainerId","useId","drawerButtonId","contextValue","useMemo"],"sources":["../../../src/context/pageCommonContext/PageCommonContext.ts","../../../src/context/pageCommonContext/PageCommonContextProvider.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { createContext } from 'react';\n\nexport type PageCommonConfig = {\n readonly afterAppBarComponent?: ReactNode;\n readonly afterAppBarRightComponent?: ReactNode;\n readonly appBarHeight?: number;\n readonly appBarIcon?: ReactNode;\n readonly beforeAppBarComponent?: ReactNode;\n readonly beforeAppBarRightComponent?: ReactNode;\n};\n\nexport type PageCommonContextData = {\n readonly drawerButtonId: string;\n readonly drawerContainerId: string;\n readonly isDrawerOpen: boolean;\n readonly isNoInteraction: boolean;\n readonly setIsDrawerOpen: (open: boolean) => void;\n readonly setIsNoInteraction: (open: boolean) => void;\n} & PageCommonConfig;\n\nexport const PageCommonContext = createContext<PageCommonContextData>({} as PageCommonContextData);\n","import {\n useId,\n useMemo,\n useState,\n} from 'react';\nimport type { PropsWithChildren } from 'react';\n\nimport type {\n PageCommonConfig,\n PageCommonContextData,\n} from './PageCommonContext';\nimport { PageCommonContext } from './PageCommonContext';\n\nexport type PageCommonContextProviderProps = PropsWithChildren<PageCommonConfig>;\n\nexport const PageCommonContextProvider = ({ children, ...props }: PageCommonContextProviderProps) => {\n const [isDrawerOpen, setIsDrawerOpen] = useState(false);\n const [isNoInteraction, setIsNoInteraction] = useState(false);\n const drawerContainerId = useId();\n const drawerButtonId = useId();\n\n const contextValue = useMemo<PageCommonContextData>(() => {\n return {\n ...props,\n drawerButtonId,\n drawerContainerId,\n isDrawerOpen,\n isNoInteraction,\n setIsDrawerOpen,\n setIsNoInteraction,\n };\n }, [props, drawerButtonId, drawerContainerId, isDrawerOpen, isNoInteraction]);\n\n return (\n <PageCommonContext value={contextValue}>\n { children }\n </PageCommonContext>\n );\n};\n"],"mappings":";;;AAqBA,IAAaA,IAAoBC,EAAqC,CAAC,CAA0B,GCNpFC,KAA6B,EAAE,aAAU,GAAGC,QAA4C;CACnG,IAAM,CAACC,GAAcC,KAAmBC,EAAS,EAAK,GAChD,CAACC,GAAiBC,KAAsBF,EAAS,EAAK,GACtDG,IAAoBC,EAAM,GAC1BC,IAAiBD,EAAM,GAEvBE,IAAeC,SACZ;EACL,GAAGV;EACH;EACA;EACA;EACA;EACA;EACA;CACF,IACC;EAACA;EAAOQ;EAAgBF;EAAmBL;EAAcG;CAAe,CAAC;CAE5E,OACE,kBAACP,GAAD;EAAmB,OAAOY;EACtB;CACe,CAAA;AAEvB"}
|
|
File without changes
|
|
@@ -7,6 +7,7 @@ export declare class MetaDataUtil {
|
|
|
7
7
|
bestColumn: null | string;
|
|
8
8
|
matchCounts: Record<string, number>;
|
|
9
9
|
};
|
|
10
|
+
static getGroupId(metaData: MetadataParseResult): string;
|
|
10
11
|
static getHighlightedIds(query: string, allAccessions: string[], metadataRecords: MetadataRecord[], matchKeyField?: null | string): string[];
|
|
11
12
|
/**
|
|
12
13
|
* Count FASTA sequence ID matches for each metadata column.
|
|
@@ -16,5 +17,5 @@ export declare class MetaDataUtil {
|
|
|
16
17
|
* Retrieve metadata record for a given sample/node name with fallback strategies.
|
|
17
18
|
*/
|
|
18
19
|
static readonly getRecordForSample: (metadata: MetadataParseResult | null | undefined, sampleId: string, matchKeyField?: null | string) => MetadataRecord | undefined;
|
|
19
|
-
static parseTsv
|
|
20
|
+
static parseTsv(content: string): MetadataParseResult;
|
|
20
21
|
}
|
|
@@ -42,6 +42,9 @@ var e = class e {
|
|
|
42
42
|
matchCounts: i
|
|
43
43
|
};
|
|
44
44
|
};
|
|
45
|
+
static getGroupId(e) {
|
|
46
|
+
return e?.columns?.includes("groupId") ? Object.keys(e.recordsByColumn.groupId)?.[0] ?? "" : "";
|
|
47
|
+
}
|
|
45
48
|
static getHighlightedIds(e, t, n, r = null) {
|
|
46
49
|
let i = e.trim().toLowerCase();
|
|
47
50
|
if (!i) return [];
|
|
@@ -81,7 +84,7 @@ var e = class e {
|
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
86
|
};
|
|
84
|
-
static parseTsv
|
|
87
|
+
static parseTsv(e) {
|
|
85
88
|
if (!e?.trim()) return {
|
|
86
89
|
columns: [],
|
|
87
90
|
error: "Empty metadata content provided.",
|
|
@@ -125,7 +128,7 @@ var e = class e {
|
|
|
125
128
|
recordsByColumn: c,
|
|
126
129
|
recordsBySampleId: s
|
|
127
130
|
};
|
|
128
|
-
}
|
|
131
|
+
}
|
|
129
132
|
};
|
|
130
133
|
export { e as MetaDataUtil };
|
|
131
134
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["MetaDataUtil","matchCounts","columns","recordsByColumn","fastaIds","bestColumn","maxMatches","prioritizedKeys","col","count","currentPriority","newPriority","pKey","found","c","trimmed","query","matched","allAccessions","acc","metadataRecords","matchKeyField","rec","val","colDict","id","metadata","sampleId","\"displayName\"","\"accessionVersion\"","match","content","lines","headerLine","delimiter","records","recordsByAccession","recordsBySampleId","i","line","values","record","colIdx","accessionKey","sampleIdKey"],"sources":["../../../src/utils/MetaDataUtil/MetaDataUtil.ts"],"sourcesContent":["import type {\n MetadataParseResult,\n MetadataRecord,\n} from '../../models/metadata.models';\n\nexport class MetaDataUtil {\n /**\n * Compute the best matching metadata column for the given FASTA sequence IDs.\n */\n public static findBestMatchingColumn = (\n columns: string[],\n recordsByColumn: Record<string, Record<string, MetadataRecord>>,\n fastaIds: string[],\n ): { bestColumn: null | string; matchCounts: Record<string, number> } => {\n const matchCounts = MetaDataUtil.getMatchCounts(columns, recordsByColumn, fastaIds);\n let bestColumn: null | string = null;\n let maxMatches = -1;\n\n if (!columns || !recordsByColumn || !fastaIds || fastaIds.length === 0) {\n return { bestColumn: null, matchCounts: {} };\n }\n\n const prioritizedKeys = [\n 'displayname',\n 'accessionversion',\n 'accession',\n 'sampleid',\n 'sample_id',\n 'id',\n 'strain',\n 'isolate',\n 'submissionid',\n 'name',\n ];\n\n for (const col of columns) {\n const colDict = recordsByColumn[col];\n if (!colDict) {\n continue;\n }\n\n const count = matchCounts[col];\n\n if (count > maxMatches) {\n maxMatches = count;\n bestColumn = col;\n } else if (count === maxMatches && count > 0 && bestColumn) {\n // Tie-breaker: prioritize standard ID column names\n const currentPriority = prioritizedKeys.indexOf(bestColumn.toLowerCase());\n const newPriority = prioritizedKeys.indexOf(col.toLowerCase());\n if (newPriority !== -1 && (currentPriority === -1 || newPriority < currentPriority)) {\n bestColumn = col;\n }\n }\n }\n\n if (maxMatches <= 0) {\n // If no matches, default to highest priority known ID column if available\n for (const pKey of prioritizedKeys) {\n const found = columns.find((c) => c.toLowerCase() === pKey);\n if (found) {\n bestColumn = found;\n break;\n }\n }\n if (!bestColumn && columns.length > 0) {\n bestColumn = columns[0];\n }\n }\n\n return { bestColumn, matchCounts };\n };\n\n public static getHighlightedIds(query: string, allAccessions: string[], metadataRecords: MetadataRecord[], matchKeyField: null | string = null): string[] {\n const trimmed = query.trim().toLowerCase();\n if (!trimmed) {\n return [];\n }\n\n const matched = new Set<string>();\n\n // Check accession names / fasta IDs\n allAccessions.forEach((acc) => {\n if (acc.toLowerCase().includes(trimmed)) {\n matched.add(acc);\n }\n });\n\n // Check metadata records\n metadataRecords.forEach((rec) => {\n const acc =\n (matchKeyField && rec[matchKeyField]) ||\n rec.displayName ||\n rec.accessionVersion ||\n rec.accession ||\n rec.id ||\n rec.submissionId ||\n rec.SampleID;\n if (acc) {\n const matchesAnyField = Object.values(rec).some(\n (val) => typeof val === 'string' && val.toLowerCase().includes(trimmed),\n );\n if (matchesAnyField) {\n matched.add(acc);\n }\n }\n });\n\n return Array.from(matched);\n }\n /**\n * Count FASTA sequence ID matches for each metadata column.\n */\n public static getMatchCounts = (\n columns: string[],\n recordsByColumn: Record<string, Record<string, MetadataRecord>>,\n fastaIds: string[],\n ): Record<string, number> => {\n const matchCounts: Record<string, number> = {};\n\n if (!columns || !recordsByColumn || !fastaIds || fastaIds.length === 0) {\n return matchCounts;\n }\n\n for (const col of columns) {\n const colDict = recordsByColumn[col];\n if (!colDict) {\n continue;\n }\n\n let count = 0;\n for (const id of fastaIds) {\n if (colDict[id]) {\n count++;\n }\n }\n matchCounts[col] = count;\n }\n\n return matchCounts;\n };\n\n /**\n * Retrieve metadata record for a given sample/node name with fallback strategies.\n */\n public static readonly getRecordForSample = (\n metadata: MetadataParseResult | null | undefined,\n sampleId: string,\n matchKeyField?: null | string,\n ): MetadataRecord | undefined => {\n if (!metadata || !sampleId) {\n return undefined;\n }\n\n // 1. If matchKeyField is specified and exists, check it first\n if (matchKeyField && metadata.recordsByColumn?.[matchKeyField]) {\n const rec = metadata.recordsByColumn[matchKeyField][sampleId];\n if (rec) {\n return rec;\n }\n }\n\n // 2. Check displayName\n if (metadata.recordsByColumn?.['displayName']?.[sampleId]) {\n return metadata.recordsByColumn['displayName'][sampleId];\n }\n\n // 3. Check accessionVersion\n if (metadata.recordsByColumn?.['accessionVersion']?.[sampleId]) {\n return metadata.recordsByColumn['accessionVersion'][sampleId];\n }\n\n // 4. Check accession maps\n if (metadata.recordsByAccession[sampleId]) {\n return metadata.recordsByAccession[sampleId];\n }\n if (metadata.recordsBySampleId[sampleId]) {\n return metadata.recordsBySampleId[sampleId];\n }\n\n // 5. Broad search through all indexed columns\n if (metadata.recordsByColumn) {\n for (const col of Object.keys(metadata.recordsByColumn)) {\n const match = metadata.recordsByColumn[col][sampleId];\n if (match) {\n return match;\n }\n }\n }\n\n return undefined;\n };\n\n public static parseTsv = (content: string): MetadataParseResult => {\n if (!content?.trim()) {\n return {\n columns: [],\n error: 'Empty metadata content provided.',\n records: [],\n recordsByAccession: {},\n recordsByColumn: {},\n recordsBySampleId: {},\n };\n }\n\n const lines = content.trim().split('\\n');\n if (lines.length < 2) {\n return {\n columns: [],\n error: 'Metadata file must contain at least a header row and one data row.',\n records: [],\n recordsByAccession: {},\n recordsByColumn: {},\n recordsBySampleId: {},\n };\n }\n\n // Parse header row\n const headerLine = lines[0].trim();\n let delimiter = '\\t';\n if (!headerLine.includes('\\t') && headerLine.includes(',')) {\n delimiter = ',';\n }\n const columns = headerLine\n .split(delimiter)\n .map((col) => col.trim().replace(/^[\"']|[\"']$/g, ''))\n .filter((c) => c.length > 0);\n\n const records: MetadataRecord[] = [];\n const recordsByAccession: Record<string, MetadataRecord> = {};\n const recordsBySampleId: Record<string, MetadataRecord> = {};\n const recordsByColumn: Record<string, Record<string, MetadataRecord>> = {};\n\n columns.forEach((col) => {\n recordsByColumn[col] = {};\n });\n\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i].trim();\n if (!line) {\n continue;\n }\n\n const values = line.split(delimiter).map((val) => val.trim().replace(/^[\"']|[\"']$/g, ''));\n const record: MetadataRecord = {};\n\n columns.forEach((col, colIdx) => {\n const val = values[colIdx] ?? '';\n record[col] = val;\n if (val) {\n recordsByColumn[col][val] = record;\n }\n });\n\n records.push(record);\n\n // Index by potential ID keys\n const accessionKey =\n record.displayName ||\n record.accessionVersion ||\n record.accession ||\n record.id ||\n record.submissionId ||\n record.strain ||\n record.isolate ||\n record.name;\n\n if (accessionKey) {\n recordsByAccession[accessionKey] = record;\n }\n\n const sampleIdKey =\n record.SampleID ||\n record.sampleId ||\n record.sample_id ||\n record.specimenCollectorSampleId ||\n record.id;\n\n if (sampleIdKey) {\n recordsBySampleId[sampleIdKey] = record;\n }\n }\n\n return {\n columns,\n records,\n recordsByAccession,\n recordsByColumn,\n recordsBySampleId,\n };\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["MetaDataUtil","matchCounts","columns","recordsByColumn","fastaIds","bestColumn","maxMatches","prioritizedKeys","col","count","currentPriority","newPriority","pKey","found","c","metaData","trimmed","query","matched","allAccessions","acc","metadataRecords","matchKeyField","rec","val","colDict","id","metadata","sampleId","\"displayName\"","\"accessionVersion\"","match","content","lines","headerLine","delimiter","records","recordsByAccession","recordsBySampleId","i","line","values","record","colIdx","accessionKey","sampleIdKey"],"sources":["../../../src/utils/MetaDataUtil/MetaDataUtil.ts"],"sourcesContent":["import type {\n MetadataParseResult,\n MetadataRecord,\n} from '../../models/metadata.models';\n\nexport class MetaDataUtil {\n /**\n * Compute the best matching metadata column for the given FASTA sequence IDs.\n */\n public static findBestMatchingColumn = (\n columns: string[],\n recordsByColumn: Record<string, Record<string, MetadataRecord>>,\n fastaIds: string[],\n ): { bestColumn: null | string; matchCounts: Record<string, number> } => {\n const matchCounts = MetaDataUtil.getMatchCounts(columns, recordsByColumn, fastaIds);\n let bestColumn: null | string = null;\n let maxMatches = -1;\n\n if (!columns || !recordsByColumn || !fastaIds || fastaIds.length === 0) {\n return { bestColumn: null, matchCounts: {} };\n }\n\n const prioritizedKeys = [\n 'displayname',\n 'accessionversion',\n 'accession',\n 'sampleid',\n 'sample_id',\n 'id',\n 'strain',\n 'isolate',\n 'submissionid',\n 'name',\n ];\n\n for (const col of columns) {\n const colDict = recordsByColumn[col];\n if (!colDict) {\n continue;\n }\n\n const count = matchCounts[col];\n\n if (count > maxMatches) {\n maxMatches = count;\n bestColumn = col;\n } else if (count === maxMatches && count > 0 && bestColumn) {\n // Tie-breaker: prioritize standard ID column names\n const currentPriority = prioritizedKeys.indexOf(bestColumn.toLowerCase());\n const newPriority = prioritizedKeys.indexOf(col.toLowerCase());\n if (newPriority !== -1 && (currentPriority === -1 || newPriority < currentPriority)) {\n bestColumn = col;\n }\n }\n }\n\n if (maxMatches <= 0) {\n // If no matches, default to highest priority known ID column if available\n for (const pKey of prioritizedKeys) {\n const found = columns.find((c) => c.toLowerCase() === pKey);\n if (found) {\n bestColumn = found;\n break;\n }\n }\n if (!bestColumn && columns.length > 0) {\n bestColumn = columns[0];\n }\n }\n\n return { bestColumn, matchCounts };\n };\n\n public static getGroupId(metaData: MetadataParseResult): string {\n if (!metaData?.columns?.includes('groupId')) {\n return '';\n }\n\n return Object.keys(metaData.recordsByColumn.groupId)?.[0] ?? '';\n }\n\n public static getHighlightedIds(query: string, allAccessions: string[], metadataRecords: MetadataRecord[], matchKeyField: null | string = null): string[] {\n const trimmed = query.trim().toLowerCase();\n if (!trimmed) {\n return [];\n }\n\n const matched = new Set<string>();\n\n // Check accession names / fasta IDs\n allAccessions.forEach((acc) => {\n if (acc.toLowerCase().includes(trimmed)) {\n matched.add(acc);\n }\n });\n\n // Check metadata records\n metadataRecords.forEach((rec) => {\n const acc =\n (matchKeyField && rec[matchKeyField]) ||\n rec.displayName ||\n rec.accessionVersion ||\n rec.accession ||\n rec.id ||\n rec.submissionId ||\n rec.SampleID;\n if (acc) {\n const matchesAnyField = Object.values(rec).some(\n (val) => typeof val === 'string' && val.toLowerCase().includes(trimmed),\n );\n if (matchesAnyField) {\n matched.add(acc);\n }\n }\n });\n\n return Array.from(matched);\n }\n\n /**\n * Count FASTA sequence ID matches for each metadata column.\n */\n public static getMatchCounts = (\n columns: string[],\n recordsByColumn: Record<string, Record<string, MetadataRecord>>,\n fastaIds: string[],\n ): Record<string, number> => {\n const matchCounts: Record<string, number> = {};\n\n if (!columns || !recordsByColumn || !fastaIds || fastaIds.length === 0) {\n return matchCounts;\n }\n\n for (const col of columns) {\n const colDict = recordsByColumn[col];\n if (!colDict) {\n continue;\n }\n\n let count = 0;\n for (const id of fastaIds) {\n if (colDict[id]) {\n count++;\n }\n }\n matchCounts[col] = count;\n }\n\n return matchCounts;\n };\n\n /**\n * Retrieve metadata record for a given sample/node name with fallback strategies.\n */\n public static readonly getRecordForSample = (\n metadata: MetadataParseResult | null | undefined,\n sampleId: string,\n matchKeyField?: null | string,\n ): MetadataRecord | undefined => {\n if (!metadata || !sampleId) {\n return undefined;\n }\n\n // 1. If matchKeyField is specified and exists, check it first\n if (matchKeyField && metadata.recordsByColumn?.[matchKeyField]) {\n const rec = metadata.recordsByColumn[matchKeyField][sampleId];\n if (rec) {\n return rec;\n }\n }\n\n // 2. Check displayName\n if (metadata.recordsByColumn?.['displayName']?.[sampleId]) {\n return metadata.recordsByColumn['displayName'][sampleId];\n }\n\n // 3. Check accessionVersion\n if (metadata.recordsByColumn?.['accessionVersion']?.[sampleId]) {\n return metadata.recordsByColumn['accessionVersion'][sampleId];\n }\n\n // 4. Check accession maps\n if (metadata.recordsByAccession[sampleId]) {\n return metadata.recordsByAccession[sampleId];\n }\n if (metadata.recordsBySampleId[sampleId]) {\n return metadata.recordsBySampleId[sampleId];\n }\n\n // 5. Broad search through all indexed columns\n if (metadata.recordsByColumn) {\n for (const col of Object.keys(metadata.recordsByColumn)) {\n const match = metadata.recordsByColumn[col][sampleId];\n if (match) {\n return match;\n }\n }\n }\n\n return undefined;\n };\n\n public static parseTsv(content: string): MetadataParseResult {\n if (!content?.trim()) {\n return {\n columns: [],\n error: 'Empty metadata content provided.',\n records: [],\n recordsByAccession: {},\n recordsByColumn: {},\n recordsBySampleId: {},\n };\n }\n\n const lines = content.trim().split('\\n');\n if (lines.length < 2) {\n return {\n columns: [],\n error: 'Metadata file must contain at least a header row and one data row.',\n records: [],\n recordsByAccession: {},\n recordsByColumn: {},\n recordsBySampleId: {},\n };\n }\n\n // Parse header row\n const headerLine = lines[0].trim();\n let delimiter = '\\t';\n if (!headerLine.includes('\\t') && headerLine.includes(',')) {\n delimiter = ',';\n }\n const columns = headerLine\n .split(delimiter)\n .map((col) => col.trim().replace(/^[\"']|[\"']$/g, ''))\n .filter((c) => c.length > 0);\n\n const records: MetadataRecord[] = [];\n const recordsByAccession: Record<string, MetadataRecord> = {};\n const recordsBySampleId: Record<string, MetadataRecord> = {};\n const recordsByColumn: Record<string, Record<string, MetadataRecord>> = {};\n\n columns.forEach((col) => {\n recordsByColumn[col] = {};\n });\n\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i].trim();\n if (!line) {\n continue;\n }\n\n const values = line.split(delimiter).map((val) => val.trim().replace(/^[\"']|[\"']$/g, ''));\n const record: MetadataRecord = {};\n\n columns.forEach((col, colIdx) => {\n const val = values[colIdx] ?? '';\n record[col] = val;\n if (val) {\n recordsByColumn[col][val] = record;\n }\n });\n\n records.push(record);\n\n // Index by potential ID keys\n const accessionKey =\n record.displayName ||\n record.accessionVersion ||\n record.accession ||\n record.id ||\n record.submissionId ||\n record.strain ||\n record.isolate ||\n record.name;\n\n if (accessionKey) {\n recordsByAccession[accessionKey] = record;\n }\n\n const sampleIdKey =\n record.SampleID ||\n record.sampleId ||\n record.sample_id ||\n record.specimenCollectorSampleId ||\n record.id;\n\n if (sampleIdKey) {\n recordsBySampleId[sampleIdKey] = record;\n }\n }\n\n return {\n columns,\n records,\n recordsByAccession,\n recordsByColumn,\n recordsBySampleId,\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { WindowService as t } from "@gen-epix/ui-core/classes/services/WindowSer
|
|
|
4
4
|
var n = class {
|
|
5
5
|
static clearSearchParams() {
|
|
6
6
|
let n = new URL(e.resolve(t).window.location.href);
|
|
7
|
-
n.search = "", e.resolve(t).window.history.replaceState(null, "", n.toString());
|
|
7
|
+
n.search = "", e.resolve(t).window.history.replaceState(null, "", n.toString()), e.resolve(t).window.dispatchEvent(new Event("loculus:urlparamschange"));
|
|
8
8
|
}
|
|
9
9
|
static fastaLabelFromUrl(e) {
|
|
10
10
|
try {
|
|
@@ -16,17 +16,16 @@ var n = class {
|
|
|
16
16
|
}
|
|
17
17
|
static getBaseURLWithPath() {
|
|
18
18
|
let n = new URL(e.resolve(t).window.location.href);
|
|
19
|
-
return `${n.protocol}//${n.host}/${n.pathname.split("/").filter(Boolean).shift()}`;
|
|
19
|
+
return n.pathname ? `${n.protocol}//${n.host}/${n.pathname.split("/").filter(Boolean).shift()}` : `${n.protocol}//${n.host}`;
|
|
20
20
|
}
|
|
21
|
-
static
|
|
21
|
+
static isValidResourceUrl(e) {
|
|
22
22
|
let t;
|
|
23
23
|
try {
|
|
24
24
|
t = new URL(e.trim());
|
|
25
25
|
} catch {
|
|
26
|
-
|
|
26
|
+
return !1;
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
return t.href;
|
|
28
|
+
return t.protocol === "http:" || t.protocol === "https:";
|
|
30
29
|
}
|
|
31
30
|
};
|
|
32
31
|
export { n as UrlUtil };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["UrlUtil","url","container","WindowService","urlObject","segment"],"sources":["../../../src/utils/UrlUtil/UrlUtil.ts"],"sourcesContent":["import { container } from 'tsyringe';\nimport { WindowService } from '@gen-epix/ui-core/classes/services/WindowService';\n\n\nexport class UrlUtil {\n public static clearSearchParams(): void {\n const url = new URL(container.resolve(WindowService).window.location.href);\n url.search = '';\n container.resolve(WindowService).window.history.replaceState(null, '', url.toString());\n }\n public static fastaLabelFromUrl(url: string): string {\n try {\n const urlObject = new URL(url);\n const segment = urlObject.pathname.split('/').filter(Boolean).pop();\n if (segment) {\n return decodeURIComponent(segment);\n }\n return `${urlObject.hostname}.fasta`;\n } catch {\n return 'remote-alignment.fasta';\n }\n }\n\n public static getBaseURLWithPath(): string {\n const url = new URL(container.resolve(WindowService).window.location.href);\n return `${url.protocol}//${url.host}/${url.pathname.split('/').filter(Boolean).shift()}`;\n }\n\n public static
|
|
1
|
+
{"version":3,"file":"index.js","names":["UrlUtil","url","container","WindowService","urlObject","segment"],"sources":["../../../src/utils/UrlUtil/UrlUtil.ts"],"sourcesContent":["import { container } from 'tsyringe';\nimport { WindowService } from '@gen-epix/ui-core/classes/services/WindowService';\n\n\nexport class UrlUtil {\n public static clearSearchParams(): void {\n const url = new URL(container.resolve(WindowService).window.location.href);\n url.search = '';\n container.resolve(WindowService).window.history.replaceState(null, '', url.toString());\n container.resolve(WindowService).window.dispatchEvent(new Event('loculus:urlparamschange'));\n }\n\n public static fastaLabelFromUrl(url: string): string {\n try {\n const urlObject = new URL(url);\n const segment = urlObject.pathname.split('/').filter(Boolean).pop();\n if (segment) {\n return decodeURIComponent(segment);\n }\n return `${urlObject.hostname}.fasta`;\n } catch {\n return 'remote-alignment.fasta';\n }\n }\n\n public static getBaseURLWithPath(): string {\n const url = new URL(container.resolve(WindowService).window.location.href);\n if (url.pathname) {\n return `${url.protocol}//${url.host}/${url.pathname.split('/').filter(Boolean).shift()}`;\n }\n return `${url.protocol}//${url.host}`;\n }\n\n\n public static isValidResourceUrl(url: string): boolean {\n let urlObject: URL;\n try {\n urlObject = new URL(url.trim());\n } catch {\n return false;\n }\n if (urlObject.protocol !== 'http:' && urlObject.protocol !== 'https:') {\n return false;\n }\n return true;\n }\n}\n"],"mappings":";;;AAIA,IAAaA,IAAb,MAAqB;CACnB,OAAc,oBAA0B;EACtC,IAAMC,IAAM,IAAI,IAAIC,EAAU,QAAQC,CAAa,CAAC,CAAC,OAAO,SAAS,IAAI;EAGzE,AAFA,EAAI,SAAS,IACbD,EAAU,QAAQC,CAAa,CAAC,CAAC,OAAO,QAAQ,aAAa,MAAM,IAAIF,EAAI,SAAS,CAAC,GACrFC,EAAU,QAAQC,CAAa,CAAC,CAAC,OAAO,cAAc,IAAI,MAAM,yBAAyB,CAAC;CAC5F;CAEA,OAAc,kBAAkB,GAAqB;EACnD,IAAI;GACF,IAAMC,IAAY,IAAI,IAAIH,CAAG,GACvBI,IAAUD,EAAU,SAAS,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,IAAI;GAIlE,OAHIC,IACK,mBAAmBA,CAAO,IAE5B,GAAGD,EAAU,SAAS;EAC/B,QAAQ;GACN,OAAO;EACT;CACF;CAEA,OAAc,qBAA6B;EACzC,IAAMH,IAAM,IAAI,IAAIC,EAAU,QAAQC,CAAa,CAAC,CAAC,OAAO,SAAS,IAAI;EAIzE,OAHIF,EAAI,WACC,GAAGA,EAAI,SAAS,IAAIA,EAAI,KAAK,GAAGA,EAAI,SAAS,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,MAEhF,GAAGA,EAAI,SAAS,IAAIA,EAAI;CACjC;CAGA,OAAc,mBAAmB,GAAsB;EACrD,IAAIG;EACJ,IAAI;GACF,IAAY,IAAI,IAAIH,EAAI,KAAK,CAAC;EAChC,QAAQ;GACN,OAAO;EACT;EAIA,OAHIG,EAAU,aAAa,WAAWA,EAAU,aAAa;CAI/D;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rivm-loculus-tools/common-frontend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"license": "EUPL-1.2",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"lodash": "4.18.1",
|
|
32
|
-
"zustand": "5.0.16"
|
|
32
|
+
"zustand": "5.0.16",
|
|
33
|
+
"keycloak-js": "26.2.5"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"@emotion/cache": "11.14.0",
|
|
@@ -79,10 +80,22 @@
|
|
|
79
80
|
"types": "./dist/classes/services/FastaDataEventBusService/index.d.ts",
|
|
80
81
|
"import": "./dist/classes/services/FastaDataEventBusService/index.js"
|
|
81
82
|
},
|
|
83
|
+
"./classes/services/LoculusService": {
|
|
84
|
+
"types": "./dist/classes/services/LoculusService/index.d.ts",
|
|
85
|
+
"import": "./dist/classes/services/LoculusService/index.js"
|
|
86
|
+
},
|
|
87
|
+
"./classes/services/UrlService": {
|
|
88
|
+
"types": "./dist/classes/services/UrlService/index.d.ts",
|
|
89
|
+
"import": "./dist/classes/services/UrlService/index.js"
|
|
90
|
+
},
|
|
82
91
|
"./components/app/App": {
|
|
83
92
|
"types": "./dist/components/app/App/index.d.ts",
|
|
84
93
|
"import": "./dist/components/app/App/index.js"
|
|
85
94
|
},
|
|
95
|
+
"./components/authentication/AuthenticationLoader": {
|
|
96
|
+
"types": "./dist/components/authentication/AuthenticationLoader/index.d.ts",
|
|
97
|
+
"import": "./dist/components/authentication/AuthenticationLoader/index.js"
|
|
98
|
+
},
|
|
86
99
|
"./components/fasta/FastaDataImportDialog": {
|
|
87
100
|
"types": "./dist/components/fasta/FastaDataImportDialog/index.d.ts",
|
|
88
101
|
"import": "./dist/components/fasta/FastaDataImportDialog/index.js"
|
|
@@ -107,6 +120,10 @@
|
|
|
107
120
|
"types": "./dist/components/fasta/FastaPageHeaderResetButton/index.d.ts",
|
|
108
121
|
"import": "./dist/components/fasta/FastaPageHeaderResetButton/index.js"
|
|
109
122
|
},
|
|
123
|
+
"./components/loculus/UploadToLoculusModal": {
|
|
124
|
+
"types": "./dist/components/loculus/UploadToLoculusModal/index.d.ts",
|
|
125
|
+
"import": "./dist/components/loculus/UploadToLoculusModal/index.js"
|
|
126
|
+
},
|
|
110
127
|
"./components/ui/ButtonWithMenu": {
|
|
111
128
|
"types": "./dist/components/ui/ButtonWithMenu/index.d.ts",
|
|
112
129
|
"import": "./dist/components/ui/ButtonWithMenu/index.js"
|
|
@@ -167,6 +184,10 @@
|
|
|
167
184
|
"types": "./dist/models/metadata.models.d.ts",
|
|
168
185
|
"import": "./dist/models/metadata.models.js"
|
|
169
186
|
},
|
|
187
|
+
"./models/url.models": {
|
|
188
|
+
"types": "./dist/models/url.models.d.ts",
|
|
189
|
+
"import": "./dist/models/url.models.js"
|
|
190
|
+
},
|
|
170
191
|
"./pages/ErrorPage": {
|
|
171
192
|
"types": "./dist/pages/ErrorPage/index.d.ts",
|
|
172
193
|
"import": "./dist/pages/ErrorPage/index.js"
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { CONFIG_TOKEN as e } from "../constants/config.constants.js";
|
|
2
|
-
import { t } from "./decorate.js";
|
|
3
|
-
import { inject as n, singleton as r } from "tsyringe";
|
|
4
|
-
import { WindowService as i } from "@gen-epix/ui-core/classes/services/WindowService";
|
|
5
|
-
import a from "@emotion/cache";
|
|
6
|
-
// \0@oxc-project+runtime@0.155.0/helpers/esm/decorateParam.js
|
|
7
|
-
function o(e, t) {
|
|
8
|
-
return function(n, r) {
|
|
9
|
-
t(n, r, e);
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
// src/classes/services/EmotionCacheService/EmotionCacheService.ts
|
|
13
|
-
var s = class {
|
|
14
|
-
emotionCache;
|
|
15
|
-
constructor(e, t) {
|
|
16
|
-
this.emotionCache = a({
|
|
17
|
-
container: t.document.head,
|
|
18
|
-
key: "loculus-tools",
|
|
19
|
-
nonce: e.nonce,
|
|
20
|
-
prepend: !!e.nonce,
|
|
21
|
-
stylisPlugins: []
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
s = t([
|
|
26
|
-
r(),
|
|
27
|
-
o(0, n(e)),
|
|
28
|
-
o(1, n(i))
|
|
29
|
-
], s);
|
|
30
|
-
export { s as t };
|
|
31
|
-
|
|
32
|
-
//# sourceMappingURL=EmotionCacheService.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"EmotionCacheService.js","names":["_decorate","_decorateParam","createCache","__decorateParam","target","key","paramIndex","EmotionCacheService","windowService","config","__decorate","singleton","inject","CONFIG_TOKEN","WindowService"],"sources":["../../src/classes/services/EmotionCacheService/EmotionCacheService.ts"],"sourcesContent":["import type { EmotionCache } from '@emotion/cache';\nimport createCache from '@emotion/cache';\nimport {\n inject,\n singleton,\n} from 'tsyringe';\nimport { WindowService } from '@gen-epix/ui-core/classes/services/WindowService';\n\nimport { CONFIG_TOKEN } from '../../../constants/config.constants';\nimport type { ConfigBase } from '../../../models/config.models';\n\n\n@singleton()\nexport class EmotionCacheService {\n public readonly emotionCache: EmotionCache;\n\n public constructor(\n @inject(CONFIG_TOKEN) config: ConfigBase,\n @inject(WindowService) windowService: WindowService,\n ) {\n this.emotionCache = createCache({\n container: windowService.document.head,\n key: 'loculus-tools',\n nonce: config.nonce,\n prepend: !!config.nonce,\n stylisPlugins: [],\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;AAaO,IAAMO,IAAN,MAA0B;CAC/B;CAEA,YACE,GACA,GACA;EACA,KAAK,eAAeL,EAAY;GAC9B,WAAWM,EAAc,SAAS;GAClC,KAAK;GACL,OAAOC,EAAO;GACd,SAAS,CAAC,CAACA,EAAO;GAClB,eAAe,CAAC;EAClB,CAAC;CACH;AACF;;CAhBCE,EAAU;CAKNV,EAAA,GAAAW,EAAOC,CAAY,CAAA;CACnBZ,EAAA,GAAAW,EAAOE,CAAa,CAAA"}
|
package/dist/_chunks/react.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import e from "react";
|
|
2
|
-
// ../../../node_modules/.pnpm/zustand@5.0.16_@types+react@19.3.0_react@19.3.1_use-sync-external-store@1.7.0_react@19.3.1_/node_modules/zustand/esm/react.mjs
|
|
3
|
-
var t = (e) => e;
|
|
4
|
-
function n(n, r = t) {
|
|
5
|
-
let i = e.useSyncExternalStore(n.subscribe, e.useCallback(() => r(n.getState()), [n, r]), () => r(n.getInitialState()));
|
|
6
|
-
return e.useDebugValue(i), i;
|
|
7
|
-
}
|
|
8
|
-
export { n as t };
|
|
9
|
-
|
|
10
|
-
//# sourceMappingURL=react.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"react.js","names":["React","identity","arg","useStore","slice","api","selector"],"sources":["../../../../../node_modules/.pnpm/zustand@5.0.16_@types+react@19.3.0_react@19.3.1_use-sync-external-store@1.7.0_react@19.3.1_/node_modules/zustand/esm/react.mjs"],"sourcesContent":["import React from 'react';\nimport { createStore } from 'zustand/vanilla';\n\nconst identity = (arg) => arg;\nfunction useStore(api, selector = identity) {\n const slice = React.useSyncExternalStore(\n api.subscribe,\n React.useCallback(() => selector(api.getState()), [api, selector]),\n () => selector(api.getInitialState())\n );\n React.useDebugValue(slice);\n return slice;\n}\nconst createImpl = (createState) => {\n const api = createStore(createState);\n const useBoundStore = (selector) => useStore(api, selector);\n Object.assign(useBoundStore, api);\n return useBoundStore;\n};\nconst create = ((createState) => createState ? createImpl(createState) : createImpl);\n\nexport { create, useStore };\n"],"ignoreList":[0],"mappings":";;AAGA,IAAMC,KAAY,MAAQC;AAC1B,SAASC,EAAS,GAAK,IAAWF,GAAU;CAC1C,IAAMG,IAAQJ,EAAM,qBAClBK,EAAI,WACJL,EAAM,kBAAkBM,EAASD,EAAI,SAAS,CAAC,GAAG,CAACA,GAAKC,CAAQ,CAAC,SAC3DA,EAASD,EAAI,gBAAgB,CAAC,CACtC;CAEA,OADAL,EAAM,cAAcI,CAAK,GAClBA;AACT"}
|