From 24f10e2b0a8b1a2e38ff08386bd7311da55a4c90 Mon Sep 17 00:00:00 2001 From: Anton Tranelis Date: Tue, 16 Dec 2025 17:50:29 +0100 Subject: [PATCH] linting --- lib/src/Components/Input/Autocomplete.tsx | 89 +++++++++++-------- lib/src/Components/Profile/ProfileView.tsx | 2 +- .../Subcomponents/AttestationsView.tsx | 9 +- .../Profile/Subcomponents/ProfileTagsForm.tsx | 22 +++-- .../Profile/Subcomponents/ProfileTagsView.tsx | 8 +- .../Subcomponents/TabsContainerForm.tsx | 28 +++--- .../Subcomponents/TabsContainerView.tsx | 13 +-- .../Profile/hooks/useAttestations.ts | 2 +- 8 files changed, 91 insertions(+), 82 deletions(-) diff --git a/lib/src/Components/Input/Autocomplete.tsx b/lib/src/Components/Input/Autocomplete.tsx index 12dd76cb..c2022f0f 100644 --- a/lib/src/Components/Input/Autocomplete.tsx +++ b/lib/src/Components/Input/Autocomplete.tsx @@ -1,78 +1,93 @@ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/restrict-template-expressions */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { useEffect, useRef, useState } from 'react' import { TagView } from '#components/Templates/TagView' +import type { Tag } from '#types/Tag' +import type { ChangeEvent, KeyboardEvent } from 'react' + +interface InputProps { + value: string + placeholder?: string + onKeyDown: (e: KeyboardEvent) => void + onKeyUp: () => void + onChange: (e: ChangeEvent) => void + className?: string +} + +interface AutocompleteProps { + inputProps: InputProps + suggestions: Tag[] + onSelected: (suggestion: Tag) => void + pushFilteredSuggestions?: Tag[] + setFocus?: boolean +} + export const Autocomplete = ({ inputProps, suggestions, onSelected, pushFilteredSuggestions, setFocus, -}: { - inputProps: any - suggestions: any[] - onSelected: (suggestion) => void - pushFilteredSuggestions?: any[] - setFocus?: boolean -}) => { - const [filteredSuggestions, setFilteredSuggestions] = useState([]) - const [heighlightedSuggestion, setHeighlightedSuggestion] = useState(0) +}: AutocompleteProps) => { + const [filteredSuggestions, setFilteredSuggestions] = useState([]) + const [highlightedSuggestion, setHighlightedSuggestion] = useState(0) useEffect(() => { - pushFilteredSuggestions && setFilteredSuggestions(pushFilteredSuggestions) + if (pushFilteredSuggestions) { + setFilteredSuggestions(pushFilteredSuggestions) + } }, [pushFilteredSuggestions]) useEffect(() => { - setFocus && inputRef.current?.focus() + if (setFocus) { + inputRef.current?.focus() + } }, [setFocus]) - const inputRef = useRef() + const inputRef = useRef(null) - const getSuggestions = (value) => { + const getSuggestions = (value: string): Tag[] => { const inputValue = value.trim().toLowerCase() const inputLength = inputValue.length return inputLength === 0 ? [] - : suggestions.filter((tag) => tag.name.toLowerCase().slice(0, inputLength) === inputValue) + : suggestions.filter((tag) => tag.name.toLowerCase().startsWith(inputValue)) } - const handleChange = (e) => { + const handleChange = (e: ChangeEvent) => { setFilteredSuggestions(getSuggestions(e.target.value)) - // Call the parent's onChange handler, if it exists - if (inputProps.onChange) { - inputProps.onChange(e) - } + // Call the parent's onChange handler + inputProps.onChange(e) } - function handleSuggestionClick(suggestion) { + function handleSuggestionClick(suggestion: Tag) { onSelected(suggestion) } - const handleKeyDown = (event) => { + const handleKeyDown = (event: KeyboardEvent) => { switch (event.key) { case 'ArrowDown': - heighlightedSuggestion < filteredSuggestions.length - 1 && - setHeighlightedSuggestion((current) => current + 1) + if (highlightedSuggestion < filteredSuggestions.length - 1) { + setHighlightedSuggestion((current) => current + 1) + } break case 'ArrowUp': - heighlightedSuggestion > 0 && setHeighlightedSuggestion((current) => current - 1) + if (highlightedSuggestion > 0) { + setHighlightedSuggestion((current) => current - 1) + } break case 'Enter': event.preventDefault() if (filteredSuggestions.length > 0) { // eslint-disable-next-line security/detect-object-injection - onSelected(filteredSuggestions[heighlightedSuggestion]) - setHeighlightedSuggestion(0) + onSelected(filteredSuggestions[highlightedSuggestion]) + setHighlightedSuggestion(0) + } + if (filteredSuggestions.length === 0) { + inputProps.onKeyDown(event) } - filteredSuggestions.length === 0 && inputProps.onKeyDown(event) break default: inputProps.onKeyDown(event) @@ -87,16 +102,16 @@ export const Autocomplete = ({ {...inputProps} type='text' onChange={(e) => handleChange(e)} - tabIndex='-1' + tabIndex={-1} onKeyDown={handleKeyDown} className='tw:border-none tw:focus:outline-none tw:focus:ring-0 tw:mt-5 tw:w-full' />
    0 && 'tw:bg-base-100 tw:rounded-xl tw:p-2'}`} + className={`tw:absolute tw:z-4000 ${filteredSuggestions.length > 0 ? 'tw:bg-base-100 tw:rounded-xl tw:p-2' : ''}`} > {filteredSuggestions.map((suggestion, index) => ( -
  • handleSuggestionClick(suggestion)}> - +
  • handleSuggestionClick(suggestion)}> +
  • ))}
diff --git a/lib/src/Components/Profile/ProfileView.tsx b/lib/src/Components/Profile/ProfileView.tsx index ede4d057..19327c79 100644 --- a/lib/src/Components/Profile/ProfileView.tsx +++ b/lib/src/Components/Profile/ProfileView.tsx @@ -21,8 +21,8 @@ import { useTags } from '#components/Map/hooks/useTags' import { HeaderView } from '#components/Map/Subcomponents/ItemPopupComponents/HeaderView' import { MapOverlayPage } from '#components/Templates' -import { handleDelete, linkItem, unlinkItem } from './itemFunctions' import { AttestationsContext } from './hooks/useAttestations' +import { handleDelete, linkItem, unlinkItem } from './itemFunctions' import { FlexView } from './Templates/FlexView' import { OnepagerView } from './Templates/OnepagerView' import { SimpleView } from './Templates/SimpleView' diff --git a/lib/src/Components/Profile/Subcomponents/AttestationsView.tsx b/lib/src/Components/Profile/Subcomponents/AttestationsView.tsx index 2639871a..805304a9 100644 --- a/lib/src/Components/Profile/Subcomponents/AttestationsView.tsx +++ b/lib/src/Components/Profile/Subcomponents/AttestationsView.tsx @@ -53,13 +53,16 @@ export const AttestationsView = ({ item, heading = 'Trust', hideWhenEmpty = true {getUserProfile(a.user_created.id) ? ( - +
{getUserProfile(a.user_created.id)?.image && ( Avatar )} @@ -67,7 +70,7 @@ export const AttestationsView = ({ item, heading = 'Trust', hideWhenEmpty = true
- {getUserProfile(a.user_created.id)?.name ?? a.user_created.first_name}{' '} + {getUserProfile(a.user_created.id)?.name ?? a.user_created.first_name}
{timeAgo(a.date_created)} diff --git a/lib/src/Components/Profile/Subcomponents/ProfileTagsForm.tsx b/lib/src/Components/Profile/Subcomponents/ProfileTagsForm.tsx index 61612704..e5b2b595 100644 --- a/lib/src/Components/Profile/Subcomponents/ProfileTagsForm.tsx +++ b/lib/src/Components/Profile/Subcomponents/ProfileTagsForm.tsx @@ -12,26 +12,24 @@ interface Props { placeholder?: string } -export const ProfileTagsForm = ({ - state, - setState, - dataField, - heading, - placeholder, -}: Props) => { +export const ProfileTagsForm = ({ state, setState, dataField, heading, placeholder }: Props) => { const defaultHeading = dataField === 'offers' ? 'Offers' : 'Needs' const defaultPlaceholder = dataField === 'offers' ? 'enter your offers' : 'enter your needs' return (
-

{heading ?? defaultHeading}

+

+ {heading ?? defaultHeading} +

- setState((prevState) => ({ - ...prevState, - [dataField]: tags, - })) + setState((prevState) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const updated = { ...prevState, [dataField]: tags } + return updated + }) } placeholder={placeholder ?? defaultPlaceholder} containerStyle='tw:bg-transparent tw:w-full tw:flex-1 tw:text-xs tw:pb-2 tw:overflow-auto' diff --git a/lib/src/Components/Profile/Subcomponents/ProfileTagsView.tsx b/lib/src/Components/Profile/Subcomponents/ProfileTagsView.tsx index dcc016f0..29ea74d3 100644 --- a/lib/src/Components/Profile/Subcomponents/ProfileTagsView.tsx +++ b/lib/src/Components/Profile/Subcomponents/ProfileTagsView.tsx @@ -12,16 +12,12 @@ interface Props { hideWhenEmpty?: boolean } -export const ProfileTagsView = ({ - item, - dataField, - heading, - hideWhenEmpty = true, -}: Props) => { +export const ProfileTagsView = ({ item, dataField, heading, hideWhenEmpty = true }: Props) => { const addFilterTag = useAddFilterTag() const allTags = useTags() // Get the tag IDs from the item based on dataField + // eslint-disable-next-line security/detect-object-injection const tagRelations = item[dataField] ?? [] // Resolve tag IDs to full Tag objects diff --git a/lib/src/Components/Profile/Subcomponents/TabsContainerForm.tsx b/lib/src/Components/Profile/Subcomponents/TabsContainerForm.tsx index e3b3eb54..84ebd0c6 100644 --- a/lib/src/Components/Profile/Subcomponents/TabsContainerForm.tsx +++ b/lib/src/Components/Profile/Subcomponents/TabsContainerForm.tsx @@ -6,8 +6,8 @@ import { CrowdfundingForm } from '#components/Profile/Subcomponents/Crowdfunding import { GalleryForm } from '#components/Profile/Subcomponents/GalleryForm' import { GroupSubheaderForm } from '#components/Profile/Subcomponents/GroupSubheaderForm' import { ProfileStartEndForm } from '#components/Profile/Subcomponents/ProfileStartEndForm' -import { ProfileTextForm } from '#components/Profile/Subcomponents/ProfileTextForm' import { ProfileTagsForm } from '#components/Profile/Subcomponents/ProfileTagsForm' +import { ProfileTextForm } from '#components/Profile/Subcomponents/ProfileTextForm' import type { FormState } from '#types/FormState' import type { Item } from '#types/Item' @@ -34,7 +34,7 @@ interface TabItem { title: string icon?: string // eslint-disable-next-line @typescript-eslint/no-explicit-any - items: Array<{ collection: string; id: Key | null | undefined; item: any }> + items: { collection: string; id: Key | null | undefined; item: any }[] } interface Props { @@ -42,24 +42,18 @@ interface Props { state: FormState setState: React.Dispatch> tabs: TabItem[] - icon_as_labels?: boolean + iconAsLabels?: boolean } -export const TabsContainerForm = ({ - item, - state, - setState, - tabs, - icon_as_labels = false, -}: Props) => { +export const TabsContainerForm = ({ item, state, setState, tabs, iconAsLabels = false }: Props) => { const location = useLocation() const navigate = useNavigate() const [activeTab, setActiveTab] = useState(0) - const tabsLength = tabs?.length ?? 0 + const tabsLength = tabs.length useEffect(() => { - if (!tabs || tabs.length === 0) return + if (tabs.length === 0) return const params = new URLSearchParams(location.search) const urlTab = params.get('tab') @@ -82,7 +76,7 @@ export const TabsContainerForm = ({ [location.pathname, location.search, navigate], ) - if (!tabs || tabs.length === 0) { + if (tabs.length === 0) { return null } @@ -92,7 +86,7 @@ export const TabsContainerForm = ({
{tabs.map((tab, index) => ( ))}
{/* Tab Content */}
- {tabs[activeTab]?.items.map((templateItem) => { + {/* eslint-disable-next-line security/detect-object-injection */} + {tabs[activeTab].items.map((templateItem) => { const TemplateComponent = componentMap[templateItem.collection] + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition return TemplateComponent ? ( { +export const TabsContainerView = ({ item, tabs = [], iconAsLabels = false }: Props) => { const location = useLocation() const navigate = useNavigate() const [activeTab, setActiveTab] = useState(0) @@ -92,15 +91,17 @@ export const TabsContainerView = ({ item, tabs = [], icon_as_labels = false }: P onClick={() => updateActiveTab(index)} > {tab.icon && {tab.icon}} - {!(icon_as_labels && activeTab !== index) && {tab.title}} + {!(iconAsLabels && activeTab !== index) && {tab.title}} ))}
{/* Tab Content */}
- {tabs[activeTab]?.items.map((templateItem) => { + {/* eslint-disable-next-line security/detect-object-injection */} + {tabs[activeTab].items.map((templateItem) => { const TemplateComponent = componentMap[templateItem.collection] + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition return TemplateComponent ? ( ) : ( diff --git a/lib/src/Components/Profile/hooks/useAttestations.ts b/lib/src/Components/Profile/hooks/useAttestations.ts index 1d23ad95..ae6f9c68 100644 --- a/lib/src/Components/Profile/hooks/useAttestations.ts +++ b/lib/src/Components/Profile/hooks/useAttestations.ts @@ -8,7 +8,7 @@ export interface Attestation { shape: string date_created: string user_created: { id: string; first_name: string } - to: Array<{ directus_users_id: string }> + to: { directus_users_id: string }[] } export const AttestationsContext = createContext([])