@openneuro/app 4.37.0 → 4.38.0-alpha.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openneuro/app",
3
- "version": "4.37.0",
3
+ "version": "4.38.0-alpha.1",
4
4
  "description": "React JS web frontend for the OpenNeuro platform.",
5
5
  "license": "MIT",
6
6
  "main": "public/client.js",
@@ -16,7 +16,7 @@
16
16
  "dependencies": {
17
17
  "@apollo/client": "3.13.8",
18
18
  "@artsy/fresnel": "^1.3.1",
19
- "@bids/validator": "npm:@jsr/bids__validator@^2.0.7",
19
+ "@bids/validator": "npm:@jsr/bids__validator@^2.0.8",
20
20
  "@emotion/react": "11.11.1",
21
21
  "@emotion/styled": "11.11.0",
22
22
  "@niivue/niivue": "0.57.0",
@@ -77,5 +77,5 @@
77
77
  "publishConfig": {
78
78
  "access": "public"
79
79
  },
80
- "gitHead": "998a4fa9b988103cebd227483cede442d1b80962"
80
+ "gitHead": "c7a1350704df419d17999699e056030b9aa0bd66"
81
81
  }
@@ -0,0 +1,23 @@
1
+ export function authorsCitationList(authors: string[], limit?: number): string {
2
+ let formattedAuthors = "NO AUTHORS FOUND"
3
+ if (authors && authors.length > 0) {
4
+ if (!limit || authors.length <= limit) {
5
+ // Pre-Limit
6
+ // Join with commas, with "and" before the last author
7
+ if (authors.length === 1) {
8
+ formattedAuthors = authors[0]
9
+ } else if (authors.length === 2) {
10
+ formattedAuthors = `${authors[0]} and ${authors[1]}`
11
+ } else {
12
+ const lastAuthor = authors[authors.length - 1]
13
+ const remainingAuthors = authors.slice(0, authors.length - 1)
14
+ formattedAuthors = `${remainingAuthors.join(", ")}, and ${lastAuthor}`
15
+ }
16
+ } else {
17
+ // Limit to `limit` authors and add "et al."
18
+ const limitedAuthors = authors.slice(0, limit)
19
+ formattedAuthors = `${limitedAuthors.join(", ")}, et al.`
20
+ }
21
+ }
22
+ return formattedAuthors
23
+ }
@@ -0,0 +1,33 @@
1
+ import React from "react"
2
+ import { parseISO } from "date-fns"
3
+ import getYear from "date-fns/getYear"
4
+ import type { Dataset } from "../../types/user-types"
5
+ import { authorsCitationList } from "./authors-citation-list"
6
+
7
+ interface SearchResultsCitationProps {
8
+ dataset: Dataset
9
+ }
10
+
11
+ export const SearchResultsCitation: React.FC<SearchResultsCitationProps> = ({
12
+ dataset,
13
+ }) => {
14
+ const rawAuthors = dataset.latestSnapshot?.description?.Authors
15
+ const year = dataset.created ? getYear(parseISO(dataset.created)) : "N/A"
16
+ const datasetName = dataset.latestSnapshot?.description?.Name ||
17
+ "NO DATASET NAME FOUND"
18
+ const datasetDOI = dataset.latestSnapshot?.description?.DatasetDOI
19
+
20
+ const datasetCite = `${
21
+ authorsCitationList(rawAuthors, 4)
22
+ } (${year}). ${datasetName}. OpenNeuro. [Dataset] doi: ${
23
+ datasetDOI ? `${datasetDOI}` : "N/A"
24
+ }`
25
+
26
+ return (
27
+ <cite>
28
+ {datasetCite}
29
+ </cite>
30
+ )
31
+ }
32
+
33
+ export default SearchResultsCitation
@@ -9,53 +9,73 @@ interface IterableObject {
9
9
  [s: number]: number | string | boolean | IterableObject
10
10
  }
11
11
 
12
- interface Props {
13
- property: number | string | boolean | IterableObject
12
+ interface RecursivePropertyProps {
13
+ property: number | string | boolean | IterableObject | null
14
14
  propertyName: string
15
15
  emptyPropertyLabel?: string
16
16
  rootProperty?: boolean
17
17
  propertyNameProcessor?: (name: string) => string
18
18
  }
19
19
 
20
- export const RecursiveProperty: React.FC<Props> = (props) => {
20
+ export const RecursiveProperty = ({
21
+ property,
22
+ propertyName,
23
+ emptyPropertyLabel,
24
+ rootProperty,
25
+ propertyNameProcessor,
26
+ }: RecursivePropertyProps) => {
27
+ // Determine if the property is "falsy - empty" but should be visible
28
+ const isEmptyValue = property === null ||
29
+ property === 0 ||
30
+ property === 0.0 ||
31
+ property === false ||
32
+ property === ""
33
+
21
34
  return (
22
35
  <div className="json-container">
23
- {props.property
36
+ {property !== undefined && (property || isEmptyValue)
24
37
  ? (
25
- typeof props.property === "number" ||
26
- typeof props.property === "string" ||
27
- typeof props.property === "boolean"
38
+ typeof property === "number" ||
39
+ typeof property === "string" ||
40
+ typeof property === "boolean"
28
41
  ? (
29
42
  <>
30
43
  <span className="prop-name">
31
- {props.propertyNameProcessor!(props.propertyName)}:{" "}
44
+ {propertyNameProcessor!(propertyName)}:{" "}
32
45
  </span>
33
- {props.property.toString()}
46
+ {property === null ? "null" : property.toString()}
34
47
  </>
35
48
  )
36
49
  : (
37
50
  <ExpandableProperty
38
- title={props.propertyNameProcessor!(props.propertyName)}
39
- expanded={!!props.rootProperty}
51
+ title={propertyNameProcessor!(propertyName)}
52
+ expanded={!!rootProperty}
40
53
  >
41
- {Object.values(props.property).map(
42
- /* eslint-disable-next-line */
43
- (property, index, { length }) => (
44
- <RecursiveProperty
45
- key={index}
46
- property={property}
47
- propertyName={Object.getOwnPropertyNames(
48
- props.property,
49
- )[index]}
50
- propertyNameProcessor={props.propertyNameProcessor}
51
- />
52
- ),
53
- )}
54
+ {Object.getOwnPropertyNames(property).length > 0
55
+ ? (
56
+ Object.values(property).map(
57
+ (propValue, index) => (
58
+ <RecursiveProperty
59
+ key={index}
60
+ property={propValue as
61
+ | number
62
+ | string
63
+ | boolean
64
+ | IterableObject}
65
+ propertyName={Object.getOwnPropertyNames(
66
+ property,
67
+ )[index]}
68
+ propertyNameProcessor={propertyNameProcessor}
69
+ />
70
+ ),
71
+ )
72
+ )
73
+ : <span className="empty-object-label">No properties</span>}
54
74
  </ExpandableProperty>
55
75
  )
56
76
  )
57
77
  : (
58
- props.emptyPropertyLabel
78
+ property === undefined ? emptyPropertyLabel : null
59
79
  )}
60
80
  </div>
61
81
  )
@@ -4,6 +4,7 @@ import { modalityShortMapping } from "../../components/formatting/modality-label
4
4
  import "./modality-hexagon.scss"
5
5
  interface ModalityHexagonProps {
6
6
  primaryModality: string | null | undefined
7
+ size?: string
7
8
  }
8
9
 
9
10
  //ModalityHexagon component displays a colored hexagon and label
@@ -11,6 +12,7 @@ interface ModalityHexagonProps {
11
12
 
12
13
  export const ModalityHexagon: FC<ModalityHexagonProps> = ({
13
14
  primaryModality,
15
+ size,
14
16
  }) => {
15
17
  const hexagonClass = primaryModality
16
18
  ? primaryModality.toLowerCase()
@@ -20,8 +22,10 @@ export const ModalityHexagon: FC<ModalityHexagonProps> = ({
20
22
  ? modalityShortMapping(primaryModality)
21
23
  : <i className="fa fa-circle-o-notch fa-spin"></i>
22
24
 
25
+ const effectiveSizeClass = size || ""
26
+
23
27
  return (
24
- <div className="hexagon-wrapper">
28
+ <div className={`hexagon-wrapper ${effectiveSizeClass}`}>
25
29
  <div className={`hexagon ${hexagonClass}`}></div>
26
30
  <div className="label">{labelText}</div>
27
31
  </div>
@@ -49,45 +49,96 @@
49
49
  transform: rotateZ(-60deg);
50
50
  }
51
51
 
52
- &.mri { background-color: $mri-theme; }
53
- &.eeg { background-color: $on-light-green; } // EEG uses on-light-green
54
- &.pet { background-color: $pet-theme; }
55
- &.ieeg { background-color: $ieeg-theme; }
56
- &.meg { background-color: $meg-theme; }
57
- &.nirs { background-color: $nirs-theme; }
52
+ &.mri {
53
+ background-color: $mri-theme;
54
+ }
55
+ &.eeg {
56
+ background-color: $on-light-green;
57
+ } // EEG uses on-light-green
58
+ &.pet {
59
+ background-color: $pet-theme;
60
+ }
61
+ &.ieeg {
62
+ background-color: $ieeg-theme;
63
+ }
64
+ &.meg {
65
+ background-color: $meg-theme;
66
+ }
67
+ &.nirs {
68
+ background-color: $nirs-theme;
69
+ }
70
+ }
71
+ &.small {
72
+ margin-left: 10px;
73
+ width: 28px;
74
+ height: 28px;
75
+ div.label {
76
+ font-size: 10px;
77
+ font-weight: normal;
78
+ }
58
79
  }
59
80
  }
60
81
 
82
+
83
+
61
84
  a {
62
85
  .hexagon-wrapper {
63
- margin: 20px auto;
86
+ margin: 20px auto;
64
87
 
65
88
  div.label {
66
- color: unset;
89
+ color: unset;
67
90
  }
68
91
 
69
92
  .hexagon {
70
- transition: color 0.3s;
71
- background-color: #fff;
93
+ transition: color 0.3s;
94
+ background-color: #fff;
72
95
 
73
96
  // Modality specific colors (when inside 'a')
74
- &.mri { background-color: #fff; color: $mri-theme; }
75
- &.eeg { background-color: #fff; color: $eeg-theme; }
76
- &.pet { background-color: #fff; color: $pet-theme; }
77
- &.ieeg { background-color: #fff; color: $ieeg-theme; }
78
- &.meg { background-color: #fff; color: $meg-theme; }
79
- &.nirs { background-color: #fff; color: $nirs-theme; }
97
+ &.mri {
98
+ background-color: #fff;
99
+ color: $mri-theme;
100
+ }
101
+ &.eeg {
102
+ background-color: #fff;
103
+ color: $eeg-theme;
104
+ }
105
+ &.pet {
106
+ background-color: #fff;
107
+ color: $pet-theme;
108
+ }
109
+ &.ieeg {
110
+ background-color: #fff;
111
+ color: $ieeg-theme;
112
+ }
113
+ &.meg {
114
+ background-color: #fff;
115
+ color: $meg-theme;
116
+ }
117
+ &.nirs {
118
+ background-color: #fff;
119
+ color: $nirs-theme;
120
+ }
80
121
  }
81
122
 
82
123
  &:hover {
83
124
  .hexagon {
84
- color: lighten($on-dark-aqua, 15%);
85
- &.mri { color: lighten($mri-theme, 10%); }
86
- &.eeg { color: lighten($eeg-theme, 10%); }
87
- &.pet { color: lighten($pet-theme, 10%); }
88
- &.ieeg { color: lighten($ieeg-theme, 10%); }
89
- &.meg { color: lighten($meg-theme, 10%); }
125
+ color: lighten($on-dark-aqua, 15%);
126
+ &.mri {
127
+ color: lighten($mri-theme, 10%);
128
+ }
129
+ &.eeg {
130
+ color: lighten($eeg-theme, 10%);
131
+ }
132
+ &.pet {
133
+ color: lighten($pet-theme, 10%);
134
+ }
135
+ &.ieeg {
136
+ color: lighten($ieeg-theme, 10%);
137
+ }
138
+ &.meg {
139
+ color: lighten($meg-theme, 10%);
140
+ }
90
141
  }
91
142
  }
92
143
  }
93
- }
144
+ }
@@ -49,7 +49,7 @@ exports[`SnapshotContainer component > renders successfully 1`] = `
49
49
  href="/search/modality/MRI"
50
50
  >
51
51
  <div
52
- class="hexagon-wrapper"
52
+ class="hexagon-wrapper "
53
53
  >
54
54
  <div
55
55
  class="hexagon mri"
@@ -1042,7 +1042,7 @@ OCI-1131441 (R. Poldrack, PI) in any publications.
1042
1042
  <h5
1043
1043
  class="cite-content-block"
1044
1044
  >
1045
- J. Doe and J. Doe and J. Doe (2021). DS003-downsampled (only T1). OpenNeuro. [Dataset] doi: null
1045
+ J. Doe, J. Doe, and J. Doe (2021). DS003-downsampled (only T1). OpenNeuro. [Dataset] doi: null
1046
1046
  </h5>
1047
1047
  </div>
1048
1048
  <h5>
@@ -18,7 +18,7 @@ describe("formatCitation", () => {
18
18
  })
19
19
  it('should work with "BibTeX" input', () => {
20
20
  expect(formatCitation(snapshot, "BibTeX")).toEqual(`@dataset{ds999999:1.0.2,
21
- author = {Jane Doe and Doe, John},
21
+ author = {Jane Doe AND Doe, John},
22
22
  title = {"A Test Dataset"},
23
23
  year = {2020},
24
24
  doi = {doinumbersgohere},
@@ -5,17 +5,18 @@ import parseISO from "date-fns/parseISO"
5
5
  import { CopyToClipboard } from "react-copy-to-clipboard"
6
6
  import { Button } from "../../components/button/Button"
7
7
  import { Tooltip } from "../../components/tooltip/Tooltip"
8
+ import { authorsCitationList } from "../../components/citation/authors-citation-list"
8
9
 
9
10
  export const formatCitation = (snapshot, style) => {
10
11
  const year = getYear(parseISO(snapshot.created))
11
12
  const authors = snapshot.description.Authors
12
- ? snapshot.description.Authors.join(" and ")
13
- : "NO AUTHORS FOUND"
13
+ ? authorsCitationList(snapshot.description.Authors)
14
+ : ""
14
15
  if (style === "Text") {
15
16
  return `${authors} (${year}). ${snapshot.description.Name}. OpenNeuro. [Dataset] doi: ${snapshot.description.DatasetDOI}`
16
17
  } else if (style === "BibTeX") {
17
18
  return `@dataset{${snapshot.id},
18
- author = {${authors}},
19
+ author = {${snapshot.description.Authors.join(" AND ")}},
19
20
  title = {"${snapshot.description.Name}"},
20
21
  year = {${year}},
21
22
  doi = {${snapshot.description.DatasetDOI}},
@@ -15,6 +15,7 @@ function ErrorRoute() {
15
15
  <Routes>
16
16
  <Route path="github" element={<FourOThreePage />} />
17
17
  <Route path="orcid" element={<OrcidGeneral />} />
18
+ <Route path="orcid/unknown" element={<OrcidGeneral />} />
18
19
  <Route path="email-warning" element={<OrcidEmailWarning />} />
19
20
  <Route path="*" element={<FourOFourPage />} />
20
21
  </Routes>
@@ -3,6 +3,18 @@ import React from "react"
3
3
  const OrcidError = () => (
4
4
  <div className="panel-heading">
5
5
  <h2>There was an issue authenticating with your ORCID account.</h2>
6
+ <p>
7
+ This may be a temporary issue, please try again later. Ensure that your
8
+ ORCID profile has one email address available to OpenNeuro following{" "}
9
+ <a href="https://docs.openneuro.org/orcid.html#enabling-trusted-access-to-emails">
10
+ our documentation
11
+ </a>{" "}
12
+ and try again.
13
+ </p>
14
+ <p>
15
+ If this issue persists, please contact support and include the ORCID
16
+ account you're attempting to login with.
17
+ </p>
6
18
  </div>
7
19
  )
8
20
 
@@ -117,6 +117,7 @@ export const ADVANCED_SEARCH_DATASETS_QUERY = gql`
117
117
  TracerName
118
118
  TracerRadionuclide
119
119
  }
120
+ primaryModality
120
121
  }
121
122
  issues {
122
123
  severity
@@ -128,6 +129,7 @@ export const ADVANCED_SEARCH_DATASETS_QUERY = gql`
128
129
  description {
129
130
  Name
130
131
  Authors
132
+ DatasetDOI
131
133
  }
132
134
  }
133
135
  analytics {
@@ -1,6 +1,4 @@
1
1
  import React from "react"
2
- import getYear from "date-fns/getYear"
3
- import parseISO from "date-fns/parseISO"
4
2
  import { Link } from "react-router-dom"
5
3
  import { Tooltip } from "../../components/tooltip/Tooltip"
6
4
  import { Icon } from "../../components/icon/Icon"
@@ -11,6 +9,7 @@ import "../scss/search-result.scss"
11
9
  import activityPulseIcon from "../../../assets/activity-icon.png"
12
10
  import { hasEditPermissions } from "../../authentication/profile"
13
11
  import { ModalityHexagon } from "../../components/modality-cube/ModalityHexagon"
12
+ import { SearchResultsCitation } from "../../components/citation/search-results-citation"
14
13
 
15
14
  export const formatDate = (dateObject) =>
16
15
  new Date(dateObject).toISOString().split("T")[0]
@@ -19,6 +18,7 @@ export interface SearchResultItemProps {
19
18
  node: {
20
19
  id: string
21
20
  created: string
21
+ name: string
22
22
  uploader: {
23
23
  id: string
24
24
  name: string
@@ -243,12 +243,6 @@ export const SearchResultItem = ({
243
243
  </div>
244
244
  )
245
245
 
246
- const year = getYear(parseISO(node.created))
247
- const authors = node.latestSnapshot.description?.Authors
248
- ? node.latestSnapshot.description.Authors.join(" and ")
249
- : "NO AUTHORS FOUND"
250
- const datasetCite =
251
- `${authors} (${year}). ${node.latestSnapshot.description.Name}. OpenNeuro. [Dataset] doi: ${node.latestSnapshot.description.DatasetDOI}`
252
246
  const trimlength = 450
253
247
 
254
248
  return (
@@ -270,7 +264,7 @@ export const SearchResultItem = ({
270
264
  : node.latestSnapshot.readme)
271
265
  : ""}
272
266
  </p>
273
- <cite>{datasetCite}</cite>
267
+ <SearchResultsCitation dataset={node} />
274
268
  </div>
275
269
  </div>
276
270
 
@@ -8,6 +8,7 @@ const datasetType_available = [
8
8
  ]
9
9
 
10
10
  const datasetStatus_available = [
11
+ { label: "All", value: "All" },
11
12
  { label: "Public", value: "Public" },
12
13
  { label: "Shared with Me", value: "Shared with Me" },
13
14
  { label: "Invalid", value: "Invalid" },
@@ -172,7 +173,7 @@ const initialSearchParams: SearchParams = {
172
173
  datasetType_available,
173
174
  datasetType_selected: "All Public",
174
175
  datasetStatus_available,
175
- datasetStatus_selected: null,
176
+ datasetStatus_selected: "All",
176
177
  modality_available,
177
178
  modality_selected: null,
178
179
  ageRange: [null, null],
@@ -139,6 +139,7 @@ const SearchContainer: FC<SearchContainerProps> = ({ portalContent }) => {
139
139
  ])
140
140
 
141
141
  const { loading, data, fetchMore, variables } = useSearchResults()
142
+
142
143
  const loadMore = () => {
143
144
  fetchMore({
144
145
  variables: {
@@ -404,7 +404,7 @@ export const useSearchResults = () => {
404
404
  // fetchPolicy is workaround for stuck loading bug (https://github.com/apollographql/react-apollo/issues/3270#issuecomment-579614837)
405
405
  // TODO: find better solution
406
406
  fetchPolicy: "cache-and-network",
407
- nextFetchPolicy: "cache-first",
407
+ nextFetchPolicy: "cache-and-network",
408
408
  notifyOnNetworkStatusChange: true,
409
409
  })
410
410
  }
@@ -48,6 +48,11 @@ export interface Dataset {
48
48
  created?: string
49
49
  description?: {
50
50
  Authors: string[]
51
+ DatasetDOI?: string | null
52
+ Name: string
53
+ }
54
+ summary?: {
55
+ primaryModality?: string
51
56
  }
52
57
  }
53
58
  draft?: {
@@ -40,6 +40,7 @@ const mockDataset = {
40
40
  totalFiles: 10,
41
41
  dataProcessed: true,
42
42
  pet: null,
43
+ primaryModality: "MRI",
43
44
  },
44
45
  validation: {
45
46
  errors: [],
@@ -5,6 +5,8 @@ import { Tooltip } from "../components/tooltip/Tooltip"
5
5
  import { Icon } from "../components/icon/Icon"
6
6
  import styles from "./scss/datasetcard.module.scss"
7
7
  import type { DatasetCardProps } from "../types/user-types"
8
+ import { ModalityHexagon } from "../components/modality-cube/ModalityHexagon"
9
+ import { SearchResultsCitation } from "../components/citation/search-results-citation"
8
10
 
9
11
  export const DatasetCard: React.FC<DatasetCardProps> = (
10
12
  { dataset, hasEdit },
@@ -87,6 +89,7 @@ export const DatasetCard: React.FC<DatasetCardProps> = (
87
89
  datasetSize = `${sizeInBytes} bytes`
88
90
  }
89
91
  }
92
+
90
93
  return (
91
94
  <div
92
95
  className={styles.userDsCard}
@@ -98,6 +101,9 @@ export const DatasetCard: React.FC<DatasetCardProps> = (
98
101
  {dataset.name ? dataset.name : dataset.id}
99
102
  </a>
100
103
  </h4>
104
+ <div className={styles.userDsBody}>
105
+ <SearchResultsCitation dataset={dataset} />
106
+ </div>
101
107
  <div className={styles.userDsFooter}>
102
108
  <div className={styles.userMetawrap}>
103
109
  <span>
@@ -113,6 +119,10 @@ export const DatasetCard: React.FC<DatasetCardProps> = (
113
119
  <div className={styles.userIconwrap}>
114
120
  {activityIcon}
115
121
  {publicIcon && <div className="owner-icon-wrap">{publicIcon}</div>}
122
+ <ModalityHexagon
123
+ size={"small"}
124
+ primaryModality={dataset.latestSnapshot.summary?.primaryModality}
125
+ />
116
126
  </div>
117
127
  </div>
118
128
  </div>
@@ -1,194 +1,203 @@
1
1
  .userDsWrap {
2
- display: flex;
3
- flex-wrap: wrap;
4
- justify-content: space-between;
2
+ display: flex;
3
+ flex-wrap: wrap;
4
+ justify-content: space-between;
5
+ }
6
+
7
+ .userDsCard {
8
+ background: #fff;
9
+ border: 1px solid #ddd;
10
+ width: calc(50% - 10px);
11
+ padding: 10px;
12
+ margin-bottom: 20px;
13
+ display: flex;
14
+ justify-content: space-between;
15
+ flex-direction: column;
16
+
17
+ h4 {
18
+ margin: 0 0 15px;
19
+ font-size: 19px;
5
20
  }
6
-
7
- .userDsCard {
8
- background: #fff;
9
- border: 1px solid #ddd;
10
- width: calc(50% - 10px);
11
- padding: 10px;
12
- margin-bottom: 20px;
21
+ }
22
+ .userDsBody {
23
+ margin-bottom: 15px;
24
+ cite {
25
+ color: var(--text-citation);
26
+
27
+ }
28
+ }
29
+ .userDsFooter {
13
30
  display: flex;
14
31
  justify-content: space-between;
15
- flex-direction: column;
16
-
17
- h4 {
18
- margin: 0 0 30px;
19
- font-size: 19px;
20
- }
21
-
22
- .userDsFooter {
32
+
33
+ .userMetawrap {
34
+ width: 90%;
23
35
  display: flex;
24
- justify-content: space-between;
25
-
26
- .userMetawrap {
27
- width: 90%;
28
- display: flex;
29
- flex-direction: column;
30
-
31
- span {
32
- color: #777;
33
-
36
+ flex-direction: column;
37
+
38
+ span {
39
+ color: #777;
40
+
41
+ b {
42
+ color: #000;
43
+ }
44
+
45
+ &:first-child {
46
+ font-size: 13px;
47
+ margin-bottom: 10px;
48
+ }
49
+
50
+ &:last-child {
51
+ text-transform: uppercase;
52
+ font-weight: bold;
53
+
34
54
  b {
35
- color: #000;
36
- }
37
-
38
- &:first-child {
39
- font-size: 13px;
40
- margin-bottom: 10px;
41
- }
42
-
43
- &:last-child {
44
- text-transform: uppercase;
45
- font-weight: bold;
46
-
47
- b {
48
- text-transform: none;
49
- }
55
+ text-transform: none;
50
56
  }
51
57
  }
52
58
  }
53
-
54
- .userIconwrap {
55
- display: flex;
56
- justify-content: flex-end;
57
- align-items: center;
58
- }
59
+ }
60
+
61
+ .userIconwrap {
62
+ display: flex;
63
+ justify-content: flex-end;
64
+ align-items: center;
59
65
  }
60
66
  }
61
67
 
62
- .resultsSummary{
63
- width: 100%;
64
- font-size: 13px;
65
- margin: 10px 0 0;
68
+
69
+ .resultsSummary {
70
+ width: 100%;
71
+ font-size: 13px;
72
+ margin: 10px 0 0;
73
+ }
74
+
75
+ .userDSfilters {
76
+ display: flex;
77
+ justify-content: flex-end;
78
+ flex-wrap: wrap;
79
+ margin: 20px 0;
80
+ padding-bottom: 20px;
81
+ align-items: center;
82
+ z-index: 10000;
83
+ position: relative;
84
+ border-bottom: 2px solid #eee;
85
+
86
+ .searchLink {
87
+ margin-right: auto;
66
88
  }
67
-
68
- .userDSfilters {
89
+ .filterDiv,
90
+ .sortDiv {
91
+ cursor: pointer;
69
92
  display: flex;
70
- justify-content: flex-end;
71
- flex-wrap: wrap;
72
- margin: 20px 0;
73
- padding-bottom:20px;
74
93
  align-items: center;
75
- z-index: 10000;
76
94
  position: relative;
77
- border-bottom: 2px solid #eee;
78
-
79
- .searchLink{
80
- margin-right: auto;
95
+ margin-bottom: 5px;
81
96
  }
82
- .filterDiv,
83
- .sortDiv {
84
- cursor: pointer;
97
+
98
+ /* Caret icon */
99
+ .filterDiv span::after,
100
+ .sortDiv span::after {
101
+ content: '';
102
+ width: 0;
103
+ height: 0;
104
+ border-left: 5px solid transparent;
105
+ border-right: 5px solid transparent;
106
+ border-top: 5px solid #333;
107
+ margin-left: 10px;
108
+ transition: transform 0.3s ease;
109
+ display: inline-block;
110
+ margin-bottom: 3px;
111
+ }
112
+
113
+ .filterDiv.open span::after,
114
+ .sortDiv.open span::after {
115
+ transform: rotate(180deg); /* Rotate the caret when open */
116
+ }
117
+
118
+ .filterDropdown,
119
+ .sortDropdown {
120
+ position: relative;
121
+
122
+ ul {
123
+ position: absolute;
124
+ list-style-type: none;
85
125
  display: flex;
86
- align-items: center;
87
- position: relative;
88
- margin-bottom: 5px;
89
- }
90
-
91
- /* Caret icon */
92
- .filterDiv span::after,
93
- .sortDiv span::after {
94
- content: "";
95
- width: 0;
96
- height: 0;
97
- border-left: 5px solid transparent;
98
- border-right: 5px solid transparent;
99
- border-top: 5px solid #333;
100
- margin-left: 10px;
101
- transition: transform 0.3s ease;
102
- display: inline-block;
103
- margin-bottom: 3px;
104
- }
105
-
106
- .filterDiv.open span::after,
107
- .sortDiv.open span::after {
108
- transform: rotate(180deg); /* Rotate the caret when open */
109
- }
110
-
111
- .filterDropdown,
112
- .sortDropdown {
113
- position: relative;
114
-
115
- ul {
116
- position: absolute;
117
- list-style-type: none;
118
- display: flex;
119
- flex-direction: column;
120
- margin: 0;
121
- padding: 20px;
122
- background-color: white;
123
- border: 1px solid #ddd;
124
- box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
125
- right: 0;
126
- top: 20px;
127
- min-width: 200px;
128
- li {
129
- padding: 8px;
130
- cursor: pointer;
131
-
132
- &:hover {
133
- background-color: #f4f4f4;
134
- }
135
-
136
- &.active {
137
- font-weight: bold;
138
- color: var(--on-dark-aqua);
139
- }
126
+ flex-direction: column;
127
+ margin: 0;
128
+ padding: 20px;
129
+ background-color: white;
130
+ border: 1px solid #ddd;
131
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
132
+ right: 0;
133
+ top: 20px;
134
+ min-width: 200px;
135
+ li {
136
+ padding: 8px;
137
+ cursor: pointer;
138
+
139
+ &:hover {
140
+ background-color: #f4f4f4;
141
+ }
142
+
143
+ &.active {
144
+ font-weight: bold;
145
+ color: var(--on-dark-aqua);
140
146
  }
141
147
  }
142
148
  }
143
-
144
- .filterDropdown {
145
- ul {min-width: 120px;}
146
- }
147
- .sortDiv {
148
- margin-left: 20px;
149
- }
150
149
  }
151
150
 
152
- .userDatasetsWrapper{
153
- margin-bottom: 50px;
151
+ .filterDropdown {
152
+ ul {
153
+ min-width: 120px;
154
+ }
154
155
  }
155
- .searchInputContainer {
156
+ .sortDiv {
157
+ margin-left: 20px;
158
+ }
159
+ }
160
+
161
+ .userDatasetsWrapper {
162
+ margin-bottom: 50px;
163
+ }
164
+ .searchInputContainer {
165
+ position: relative;
166
+ display: flex;
167
+
168
+ .searchInputWrap {
156
169
  position: relative;
157
- display: flex;
158
-
159
- .searchInputWrap{
160
- position: relative;
161
170
  width: 100%;
162
- .searchInput {
163
- flex-grow: 1;
164
- padding-right: 30px;
165
- margin-right: auto;
166
- width: 100%;
167
- }
168
-
169
-
170
- .clearSearchButton {
171
- position: absolute;
172
- top: 50%;
173
- right: 5px; /* Adjust position as needed */
174
- transform: translateY(-50%);
175
- background: none;
176
- border: none;
177
- cursor: pointer;
178
- font-size: 0.8em;
179
- color: #888;
180
- padding: 0;
181
- width: 20px;
182
- height: 20px;
183
- display: flex;
184
- align-items: center;
185
- justify-content: center;
186
-
187
- &:hover {
188
- color: #333;
171
+ .searchInput {
172
+ flex-grow: 1;
173
+ padding-right: 30px;
174
+ margin-right: auto;
175
+ width: 100%;
189
176
  }
190
- }}
191
- .searchSubmitButton {
177
+
178
+ .clearSearchButton {
179
+ position: absolute;
180
+ top: 50%;
181
+ right: 5px; /* Adjust position as needed */
182
+ transform: translateY(-50%);
183
+ background: none;
184
+ border: none;
185
+ cursor: pointer;
186
+ font-size: 0.8em;
187
+ color: #888;
188
+ padding: 0;
189
+ width: 20px;
190
+ height: 20px;
191
+ display: flex;
192
+ align-items: center;
193
+ justify-content: center;
194
+
195
+ &:hover {
196
+ color: #333;
197
+ }
198
+ }
199
+ }
200
+ .searchSubmitButton {
192
201
  background-color: var(--current-theme-secondary);
193
202
  color: #fff;
194
203
  border-radius: var(--border-radius-default);
@@ -201,6 +210,4 @@
201
210
  background-color: var(--current-theme-primary);
202
211
  }
203
212
  }
204
-
205
-
206
- }
213
+ }