@openneuro/app 4.36.1 → 4.37.0-alpha.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.
Files changed (74) hide show
  1. package/package.json +5 -3
  2. package/src/scripts/components/accordion/accordion.scss +1 -1
  3. package/src/scripts/components/activity-slider/ActivitySlider.tsx +4 -22
  4. package/src/scripts/components/activity-slider/slider.scss +1 -82
  5. package/src/scripts/components/button/button.scss +1 -1
  6. package/src/scripts/components/count-toggle/count-toggle.scss +1 -1
  7. package/src/scripts/components/dropdown/dropdown.scss +1 -1
  8. package/src/scripts/components/facets/facet.scss +5 -4
  9. package/src/scripts/components/footer/footer.scss +1 -1
  10. package/src/scripts/components/front-page/front-page.scss +1 -1
  11. package/src/scripts/components/header/header.scss +1 -1
  12. package/src/scripts/components/input/input.scss +1 -1
  13. package/src/scripts/components/input/term-search.scss +1 -1
  14. package/src/scripts/components/loading/loading.scss +1 -1
  15. package/src/scripts/components/modal/modal.scss +1 -1
  16. package/src/scripts/components/modality-cube/ModalityHexagon.tsx +29 -0
  17. package/src/scripts/components/modality-cube/modality-cube.scss +1 -1
  18. package/src/scripts/components/modality-cube/modality-hexagon.scss +93 -0
  19. package/src/scripts/components/page/page.scss +1 -1
  20. package/src/scripts/components/progress-bar/progress-bar.scss +1 -1
  21. package/src/scripts/components/radio/radio.scss +2 -2
  22. package/src/scripts/components/range/TwoHandleRange.scss +3 -3
  23. package/src/scripts/components/read-more/read-more.scss +1 -1
  24. package/src/scripts/components/scss/upload-modal.scss +1 -1
  25. package/src/scripts/components/tooltip/tooltip.scss +1 -1
  26. package/src/scripts/dataset/__tests__/__snapshots__/snapshot-container.spec.tsx.snap +132 -128
  27. package/src/scripts/dataset/__tests__/draft-container.spec.tsx +136 -0
  28. package/src/scripts/dataset/common/follow-toggles.tsx +1 -1
  29. package/src/scripts/dataset/components/DatasetHeader.tsx +13 -16
  30. package/src/scripts/dataset/components/DatasetToolButton.tsx +6 -7
  31. package/src/scripts/dataset/components/DatasetTools.tsx +6 -2
  32. package/src/scripts/dataset/draft-container.tsx +30 -24
  33. package/src/scripts/dataset/files/{file-display.jsx → file-display.tsx} +32 -19
  34. package/src/scripts/dataset/routes/tab-routes-draft.tsx +6 -1
  35. package/src/scripts/dataset/routes/tab-routes-snapshot.tsx +5 -1
  36. package/src/scripts/dataset/snapshot-container.tsx +37 -43
  37. package/src/scripts/scss/dataset/dataset-page.scss +44 -120
  38. package/src/scripts/scss/variables.scss +65 -13
  39. package/src/scripts/{components/search-page → search}/__tests__/NuerobagelSearch.spec.tsx +1 -1
  40. package/src/scripts/search/components/DatasetsRadioTabs.tsx +103 -0
  41. package/src/scripts/{components/search-page → search/components}/FilterListItem.tsx +1 -1
  42. package/src/scripts/{components/search-page → search/components}/FiltersBlock.tsx +5 -8
  43. package/src/scripts/search/components/MetaListItemList.tsx +31 -0
  44. package/src/scripts/{components/search-page → search/components}/SearchPage.tsx +15 -8
  45. package/src/scripts/search/components/SearchResultDetails.tsx +167 -0
  46. package/src/scripts/{components/search-page → search/components}/SearchResultItem.tsx +57 -173
  47. package/src/scripts/search/components/SearchResultsList.tsx +45 -0
  48. package/src/scripts/{components/search-page → search/components}/SearchSort.tsx +2 -2
  49. package/src/scripts/search/filters-block-container.tsx +1 -1
  50. package/src/scripts/search/inputs/index.ts +0 -4
  51. package/src/scripts/search/inputs/sliding-radio-group.tsx +127 -0
  52. package/src/scripts/search/inputs/sort-by-select.tsx +1 -1
  53. package/src/scripts/{components/search-page → search/scss}/filters-block.scss +1 -1
  54. package/src/scripts/{components/search-page → search/scss}/search-page.scss +123 -92
  55. package/src/scripts/search/scss/search-result-details.scss +70 -0
  56. package/src/scripts/{components/search-page → search/scss}/search-result.scss +29 -56
  57. package/src/scripts/{components/search-page → search/scss}/search-sort.scss +1 -1
  58. package/src/scripts/search/scss/sliding-radio-group.scss +115 -0
  59. package/src/scripts/search/search-container.tsx +90 -24
  60. package/src/scripts/search/use-search-results.tsx +3 -0
  61. package/src/scripts/users/github-auth-button.tsx +1 -1
  62. package/src/scripts/components/scss/_variables.scss +0 -245
  63. package/src/scripts/components/search-page/SearchResultsList.tsx +0 -29
  64. package/src/scripts/dataset/files/index.tsx +0 -6
  65. package/src/scripts/search/inputs/admin-allDatasets-toggle.tsx +0 -47
  66. package/src/scripts/search/inputs/show-datasets-radios.tsx +0 -74
  67. /package/src/scripts/{components/search-page → search/components}/CommunityHeader.tsx +0 -0
  68. /package/src/scripts/{components/search-page → search/components}/FacetBlockContainerExample.tsx +0 -0
  69. /package/src/scripts/{components/search-page → search/components}/FilterDateItem.tsx +0 -0
  70. /package/src/scripts/{components/search-page → search/components}/ModalityHeader.tsx +0 -0
  71. /package/src/scripts/{components/search-page → search/components}/NeurobagelSearch.tsx +0 -0
  72. /package/src/scripts/{components/search-page → search/components}/SearchSortContainerExample.tsx +0 -0
  73. /package/src/scripts/{components/search-page → search/components}/TermListItem.tsx +0 -0
  74. /package/src/scripts/{components/search-page → search/components}/neurobagel_logo.svg +0 -0
@@ -1,21 +1,17 @@
1
1
  import React from "react"
2
- import bytes from "bytes"
2
+ import getYear from "date-fns/getYear"
3
3
  import parseISO from "date-fns/parseISO"
4
- import formatDistanceToNow from "date-fns/formatDistanceToNow"
5
4
  import { Link } from "react-router-dom"
6
5
  import { Tooltip } from "../../components/tooltip/Tooltip"
7
6
  import { Icon } from "../../components/icon/Icon"
8
7
  import { useCookies } from "react-cookie"
9
8
  import { getProfile } from "../../authentication/profile"
10
9
  import { useUser } from "../../queries/user"
11
- import "./search-result.scss"
10
+ import "../scss/search-result.scss"
12
11
  import activityPulseIcon from "../../../assets/activity-icon.png"
13
- import { ModalityLabel } from "../../components/formatting/modality-label"
14
12
  import { hasEditPermissions } from "../../authentication/profile"
15
- /**
16
- * Return an equivalent to moment(date).format('L') without moment
17
- * @param {*} dateObject
18
- */
13
+ import { ModalityHexagon } from "../../components/modality-cube/ModalityHexagon"
14
+
19
15
  export const formatDate = (dateObject) =>
20
16
  new Date(dateObject).toISOString().split("T")[0]
21
17
 
@@ -50,6 +46,7 @@ export interface SearchResultItemProps {
50
46
  latestSnapshot: {
51
47
  id: string
52
48
  size: number
49
+ readme: string
53
50
  summary: {
54
51
  pet: {
55
52
  BodyPart: string
@@ -58,6 +55,7 @@ export interface SearchResultItemProps {
58
55
  TracerName: string[]
59
56
  TracerRadionuclide: string
60
57
  }
58
+ primaryModality: string
61
59
  modalities: string[]
62
60
  sessions: []
63
61
  subjects: string[]
@@ -84,7 +82,9 @@ export interface SearchResultItemProps {
84
82
  warnings: number
85
83
  }
86
84
  description: {
85
+ Authors: string[]
87
86
  Name: string
87
+ DatasetDOI: string
88
88
  }
89
89
  }
90
90
  analytics: {
@@ -112,11 +112,15 @@ export interface SearchResultItemProps {
112
112
  ]
113
113
  }
114
114
  datasetTypeSelected?: string
115
+ onClick: (itemId: string, event: React.MouseEvent<HTMLButtonElement>) => void
116
+ isExpanded: boolean
115
117
  }
116
118
 
117
119
  export const SearchResultItem = ({
118
120
  node,
119
121
  datasetTypeSelected,
122
+ onClick,
123
+ isExpanded,
120
124
  }: SearchResultItemProps) => {
121
125
  const { user } = useUser()
122
126
  const [cookies] = useCookies()
@@ -125,96 +129,10 @@ export const SearchResultItem = ({
125
129
 
126
130
  const isAdmin = user?.admin
127
131
  const hasEdit = hasEditPermissions(node.permissions, profileSub) || isAdmin
128
-
129
- const heading = node.latestSnapshot.description?.Name
130
- ? node.latestSnapshot.description?.Name
131
- : node.id
132
- const summary = node.latestSnapshot?.summary
133
132
  const datasetId = node.id
134
- const numSessions = summary?.sessions.length > 0 ? summary.sessions.length : 1
135
- const numSubjects = summary?.subjects.length > 0 ? summary.subjects.length : 1
136
- const accessionNumber = (
137
- <span className="result-summary-meta">
138
- <strong>Openneuro Accession Number:</strong>
139
- <Link to={"/datasets/" + datasetId}>{node.id}</Link>
140
- </span>
141
- )
142
- const sessions = (
143
- <span className="result-summary-meta">
144
- <strong>Sessions:</strong>
145
- <span>{numSessions.toLocaleString()}</span>
146
- </span>
147
- )
148
-
149
- const ages = (value) => {
150
- if (value) {
151
- const ages = value.filter((x) => x)
152
- if (ages.length === 0) return "N/A"
153
- else if (ages.length === 1) return ages[0]
154
- else return `${Math.min(...ages)} - ${Math.max(...ages)}`
155
- } else return "N/A"
156
- }
157
133
 
158
- const agesRange = (
159
- <span className="result-summary-meta">
160
- <strong>
161
- {node?.metadata?.ages?.length === 1
162
- ? "Participant's Age"
163
- : "Participants' Ages"}
164
- :{" "}
165
- </strong>
166
- <span>
167
- {ages(summary?.subjectMetadata?.map((subject) => subject.age))}
168
- </span>
169
- </span>
170
- )
171
- const subjects = (
172
- <span className="result-summary-meta">
173
- <strong>Participants:</strong>
174
- <span>{numSubjects.toLocaleString()}</span>
175
- </span>
176
- )
177
- const size = (
178
- <span className="result-summary-meta">
179
- <strong>Size:</strong>
180
- <span>{bytes(node?.latestSnapshot?.size) || "unknown"}</span>
181
- </span>
182
- )
183
- const files = (
184
- <span className="result-summary-meta">
185
- <strong>Files:</strong>
186
- <span>{summary?.totalFiles.toLocaleString()}</span>
187
- </span>
188
- )
134
+ const heading = node.latestSnapshot.description?.Name?.trim() || datasetId
189
135
 
190
- const dateAdded = formatDate(node.created)
191
- const dateAddedDifference = formatDistanceToNow(parseISO(node.created))
192
- let lastUpdatedDate
193
- if (node.snapshots.length) {
194
- const dateUpdated = formatDate(
195
- node.snapshots[node.snapshots.length - 1].created,
196
- )
197
- const dateUpdatedDifference = formatDistanceToNow(
198
- parseISO(node.snapshots[node.snapshots.length - 1].created),
199
- )
200
-
201
- lastUpdatedDate = (
202
- <>
203
- <span className="updated-divider">|</span>
204
- <div className="updated-date">
205
- <span>Updated:</span>
206
- {dateUpdated} - {dateUpdatedDifference} ago
207
- </div>
208
- </>
209
- )
210
- }
211
-
212
- const uploader = (
213
- <div className="uploader">
214
- <span>Uploaded by:</span>
215
- {node.uploader?.name} on {dateAdded} - {dateAddedDifference} ago
216
- </div>
217
- )
218
136
  const downloads = node.analytics.downloads
219
137
  ? node.analytics.downloads.toLocaleString() + " Downloads \n"
220
138
  : ""
@@ -292,33 +210,12 @@ export const SearchResultItem = ({
292
210
  </Tooltip>
293
211
  )
294
212
 
295
- const _list = (type, items) => {
296
- if (items && items.length > 0) {
297
- return (
298
- <>
299
- <strong>{type}:</strong>
300
- <div>
301
- {items.map((item, index) => (
302
- <span className="list-item" key={index}>
303
- {item}
304
- </span>
305
- ))}
306
- </div>
307
- </>
308
- )
309
- } else {
310
- return null
311
- }
312
- }
313
-
314
213
  let invalid = false
315
- // Legacy issues still flagged
316
214
  if (node.latestSnapshot.issues) {
317
- invalid = node.latestSnapshot.issues.some((issue) =>
318
- issue.severity === "error"
215
+ invalid = node.latestSnapshot.issues.some(
216
+ (issue) => issue.severity === "error",
319
217
  )
320
218
  } else {
321
- // Test if there's any schema validator errors
322
219
  invalid = node.latestSnapshot.validation?.errors > 0
323
220
  }
324
221
  const shared = !node.public && node.uploader?.id !== profileSub
@@ -346,74 +243,61 @@ export const SearchResultItem = ({
346
243
  </div>
347
244
  )
348
245
 
349
- const modalityList = summary?.modalities.length
350
- ? (
351
- <div className="modality-list">
352
- {_list(
353
- <>{summary?.modalities.length === 1 ? "Modality" : "Modalities"}</>,
354
- summary?.modalities.map((modality) => (
355
- <ModalityLabel key={modality} modality={modality} />
356
- )),
357
- )}
358
- </div>
359
- )
360
- : null
361
- const taskList = summary?.tasks.length
362
- ? <div className="task-list">{_list(<>Tasks</>, summary?.tasks)}</div>
363
- : null
364
-
365
- const tracers = summary?.pet?.TracerName?.length
366
- ? (
367
- <div className="tracers-list">
368
- {_list(
369
- <>
370
- {summary?.pet?.TracerName.length === 1
371
- ? "Radiotracer"
372
- : "Radiotracers"}
373
- </>,
374
- summary?.pet?.TracerName,
375
- )}
376
- </div>
377
- )
378
- : null
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
+ const trimlength = 450
379
253
 
380
254
  return (
381
255
  <>
382
- <div className="grid grid-nogutter search-result">
256
+ <div
257
+ className={`grid grid-nogutter search-result ${
258
+ isExpanded ? "expanded" : ""
259
+ }`}
260
+ >
383
261
  <div className="col col-9">
384
- <h3>
385
- <Link to={"/datasets/" + datasetId}>{heading}</Link>
386
- </h3>
387
- <div className="result-upload-info">
388
- {uploader}
389
- {lastUpdatedDate}
262
+ <div className="col col-12">
263
+ <h3>
264
+ <Link to={"/datasets/" + datasetId}>{heading}</Link>
265
+ </h3>
266
+ <p>
267
+ {node.latestSnapshot?.readme
268
+ ? (node.latestSnapshot.readme.length > trimlength
269
+ ? `${node.latestSnapshot.readme.substring(0, trimlength)}...`
270
+ : node.latestSnapshot.readme)
271
+ : ""}
272
+ </p>
273
+ <cite>{datasetCite}</cite>
390
274
  </div>
391
275
  </div>
392
276
 
393
- <div className="col col-3 col-sm">
277
+ <div className="col col-3 grid">
278
+ <div className="col col-12 result-icon-wrap">
279
+ {datasetOwenerIcons}
280
+ {activityIcon}
281
+ <ModalityHexagon
282
+ primaryModality={node.latestSnapshot.summary?.primaryModality}
283
+ />
284
+ </div>
394
285
  {MyDatasetsPage && (
395
- <div className="dataset-permissions-tag">
286
+ <div className="col col-12 dataset-permissions-tag text-right">
396
287
  <small>Access: {datasetPerms}</small>
397
288
  </div>
398
289
  )}
399
- <div className="result-icon-wrap">
400
- {datasetOwenerIcons}
401
- {activityIcon}
290
+ <div className="col col-12 result-actions">
291
+ <button
292
+ className={`on-button on-button--small ${
293
+ isExpanded && "expanded"
294
+ }`}
295
+ onClick={(e) => onClick(node.id, e)}
296
+ >
297
+ {isExpanded ? "Hide Details" : "Show Details"}
298
+ </button>
402
299
  </div>
403
300
  </div>
404
- <div className="col col-12 result-meta-body">
405
- {modalityList}
406
- {taskList}
407
- {tracers}
408
- </div>
409
- <div className="result-meta-footer">
410
- {accessionNumber}
411
- {sessions}
412
- {subjects}
413
- {agesRange}
414
- {size}
415
- {files}
416
- </div>
417
301
  </div>
418
302
  </>
419
303
  )
@@ -0,0 +1,45 @@
1
+ import React from "react"
2
+ import { SearchResultItem } from "./SearchResultItem"
3
+ import "../scss/search-page.scss"
4
+ import type { SearchResultItemProps } from "./SearchResultItem"
5
+
6
+ export interface SearchResultsListProps {
7
+ items: { node: SearchResultItemProps["node"] }[]
8
+ datasetTypeSelected: string
9
+ clickedItemData: SearchResultItemProps["node"] | null
10
+ handleItemClick: (
11
+ itemId: string,
12
+ event: React.MouseEvent<HTMLButtonElement>,
13
+ ) => void
14
+ }
15
+
16
+ export const SearchResultsList = ({
17
+ items,
18
+ datasetTypeSelected,
19
+ clickedItemData,
20
+ handleItemClick,
21
+ }: SearchResultsListProps) => {
22
+ return (
23
+ <>
24
+ <div
25
+ className={`search-results col col-12 ${clickedItemData ? "open" : ""}`}
26
+ >
27
+ {items.map((data) => {
28
+ if (data) {
29
+ const isActive = clickedItemData?.id === data.node.id
30
+ return (
31
+ <SearchResultItem
32
+ node={data.node}
33
+ key={data.node.id}
34
+ datasetTypeSelected={datasetTypeSelected}
35
+ onClick={handleItemClick}
36
+ isExpanded={isActive}
37
+ />
38
+ )
39
+ }
40
+ return null
41
+ })}
42
+ </div>
43
+ </>
44
+ )
45
+ }
@@ -1,6 +1,6 @@
1
1
  import React from "react"
2
- import { Dropdown } from "../dropdown/Dropdown"
3
- import "./search-sort.scss"
2
+ import { Dropdown } from "../../components/dropdown/Dropdown"
3
+ import "../scss/search-sort.scss"
4
4
 
5
5
  export interface SearchSortProps {
6
6
  items: {
@@ -13,7 +13,7 @@ import {
13
13
  SearchParamsCtx,
14
14
  useCheckIfParamsAreSelected,
15
15
  } from "./search-params-ctx"
16
- import { FiltersBlock } from "../components/search-page/FiltersBlock"
16
+ import { FiltersBlock } from "./components/FiltersBlock"
17
17
  import initialSearchParams from "./initial-search-params"
18
18
 
19
19
  interface FiltersBlockContainerProps {
@@ -1,8 +1,6 @@
1
1
  import KeywordInput from "./keyword-input"
2
- import AllDatasetsToggle from "./admin-allDatasets-toggle"
3
2
  import ModalitySelect from "./modality-select"
4
3
  import InitiativeSelect from "./initiative-select"
5
- import ShowDatasetRadios from "./show-datasets-radios"
6
4
  import AgeRangeInput from "./age-range-input"
7
5
  import SubjectCountRangeInput from "./subject-count-range-input"
8
6
  import DatasetTypeSelect from "./dataset-type-select"
@@ -23,7 +21,6 @@ import SortBySelect from "./sort-by-select"
23
21
 
24
22
  export {
25
23
  AgeRangeInput,
26
- AllDatasetsToggle,
27
24
  AuthorInput,
28
25
  BodyPartsInput,
29
26
  DatasetTypeSelect,
@@ -36,7 +33,6 @@ export {
36
33
  ScannerManufacturersModelNames,
37
34
  SectionSelect,
38
35
  SexRadios,
39
- ShowDatasetRadios,
40
36
  SortBySelect,
41
37
  SpeciesSelect,
42
38
  StudyDomainInput,
@@ -0,0 +1,127 @@
1
+ import React, { useCallback, useEffect, useRef, useState } from "react"
2
+ import type { FC } from "react"
3
+ import "../scss/sliding-radio-group.scss"
4
+
5
+ interface RadioItem {
6
+ value: string
7
+ label: string
8
+ }
9
+
10
+ interface SlidingRadioGroupProps {
11
+ items: (string | RadioItem)[]
12
+ selected: string | undefined
13
+ setSelected: (value: string) => void
14
+ groupName: string
15
+ className?: string
16
+ initialSelectedValueOverride?: string
17
+ }
18
+
19
+ const SlidingRadioGroup: FC<SlidingRadioGroupProps> = ({
20
+ items,
21
+ selected,
22
+ setSelected,
23
+ groupName,
24
+ className = "",
25
+ initialSelectedValueOverride,
26
+ }) => {
27
+ const containerRef = useRef<HTMLDivElement>(null)
28
+ const radioLabelRefs = useRef<{ [key: string]: HTMLLabelElement }>({})
29
+ const [sliderStyles, setSliderStyles] = useState({
30
+ left: 0,
31
+ width: 0,
32
+ height: 0,
33
+ top: 0,
34
+ })
35
+
36
+ const calculateSliderPosition = useCallback((targetValue: string) => {
37
+ if (!containerRef.current || !radioLabelRefs.current[targetValue]) {
38
+ return { left: 0, width: 0, height: 0, top: 0 }
39
+ }
40
+
41
+ const targetLabel = radioLabelRefs.current[targetValue]
42
+ const containerRect = containerRef.current.getBoundingClientRect()
43
+ const labelRect = targetLabel.getBoundingClientRect()
44
+
45
+ return {
46
+ left: labelRect.left - containerRect.left,
47
+ width: labelRect.width,
48
+ height: labelRect.height,
49
+ top: labelRect.top - containerRect.top,
50
+ }
51
+ }, [])
52
+
53
+ useEffect(() => {
54
+ const updateSlider = () => {
55
+ const valueToCalculate = initialSelectedValueOverride || selected
56
+
57
+ if (valueToCalculate) {
58
+ const newStyles = calculateSliderPosition(valueToCalculate)
59
+ setSliderStyles(newStyles)
60
+ } else {
61
+ // If nothing is selected, hide the slider
62
+ setSliderStyles({ left: 0, width: 0, height: 0, top: 0 })
63
+ }
64
+ }
65
+
66
+ const timeoutId = setTimeout(updateSlider, 50)
67
+ window.addEventListener("resize", updateSlider)
68
+
69
+ return () => {
70
+ clearTimeout(timeoutId)
71
+ window.removeEventListener("resize", updateSlider)
72
+ }
73
+ }, [selected, calculateSliderPosition, initialSelectedValueOverride])
74
+
75
+ const isSliderVisible = sliderStyles.width > 0 && sliderStyles.height > 0
76
+
77
+ return (
78
+ <div
79
+ className={`sliding-radio-group-root ${className} btn-group-wrapper facet-radio show-dataset-radios-container`}
80
+ ref={containerRef}
81
+ >
82
+ {/* The sliding highlight element */}
83
+ <div
84
+ className="sliding-highlight"
85
+ style={{
86
+ left: sliderStyles.left,
87
+ width: sliderStyles.width,
88
+ height: sliderStyles.height - 2,
89
+ top: sliderStyles.top,
90
+ opacity: isSliderVisible ? 1 : 0,
91
+ }}
92
+ />
93
+
94
+ <div className="show-dataset-radios-group" role="radiogroup">
95
+ {items.map((item) => {
96
+ const value = typeof item === "object" ? item.value : item
97
+ const label = typeof item === "object" ? item.label : item
98
+ const isChecked = selected === value
99
+
100
+ return (
101
+ <div className="dataset-filter-radio" key={value}>
102
+ <input
103
+ type="radio"
104
+ id={`${groupName}-${value.replace(/\s/g, "")}`}
105
+ name={groupName}
106
+ value={value}
107
+ checked={isChecked}
108
+ onChange={() => setSelected(value)}
109
+ />
110
+ <label
111
+ htmlFor={`${groupName}-${value.replace(/\s/g, "")}`}
112
+ ref={(el) => {
113
+ if (el) radioLabelRefs.current[value] = el
114
+ }}
115
+ className={isChecked ? "is-active" : ""}
116
+ >
117
+ {label}
118
+ </label>
119
+ </div>
120
+ )
121
+ })}
122
+ </div>
123
+ </div>
124
+ )
125
+ }
126
+
127
+ export default SlidingRadioGroup
@@ -1,7 +1,7 @@
1
1
  import React, { useContext } from "react"
2
2
  import type { FC } from "react"
3
3
  import { SearchParamsCtx } from "../search-params-ctx"
4
- import { SearchSort } from "../../components/search-page/SearchSort"
4
+ import { SearchSort } from "../components/SearchSort"
5
5
 
6
6
  interface SortBySelectProps {
7
7
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
@@ -1,4 +1,4 @@
1
- @import '../scss/variables';
1
+ @import '../../scss/variables';
2
2
 
3
3
  .filters-block {
4
4
  position: relative;