diff --git a/src/renderer/entities/transaction/ui/QrCode/QrReader/QrReaderWrapper.tsx b/src/renderer/entities/transaction/ui/QrCode/QrReader/QrReaderWrapper.tsx index 4b54c5feba..7a2f8d8d18 100644 --- a/src/renderer/entities/transaction/ui/QrCode/QrReader/QrReaderWrapper.tsx +++ b/src/renderer/entities/transaction/ui/QrCode/QrReader/QrReaderWrapper.tsx @@ -3,8 +3,8 @@ import { useEffect, useState } from 'react'; import { type HexString } from '@/shared/core'; import { useI18n } from '@/shared/i18n'; import { ValidationErrors, cnTw } from '@/shared/lib/utils'; -import { Button, CaptionText, Countdown, FootnoteText, Select, Shimmering, SmallTitleText } from '@/shared/ui'; -import { type DropdownOption, type DropdownResult } from '@/shared/ui/types'; +import { Button, CaptionText, Countdown, FootnoteText, Shimmering, SmallTitleText } from '@/shared/ui'; +import { Select } from '@/shared/ui-kit'; import { CameraAccessErrors, CameraError, WhiteTextButtonStyle } from '../common/constants'; import { type ErrorObject, type Progress, QrError, type VideoInput } from '../common/types'; @@ -42,8 +42,8 @@ export const QrReaderWrapper = ({ className, onResult, countdown, validationErro const [progress, setProgress] = useState(); const [isSuccess, setIsSuccess] = useState(false); - const [activeCamera, setActiveCamera] = useState>(); - const [availableCameras, setAvailableCameras] = useState[]>([]); + const [activeCamera, setActiveCamera] = useState(); + const [availableCameras, setAvailableCameras] = useState[]>([]); useEffect(() => { if (validationError) { @@ -54,18 +54,16 @@ export const QrReaderWrapper = ({ className, onResult, countdown, validationErro const isCameraOn = !(error && CameraAccessErrors.includes(error)); const onCameraList = (cameras: VideoInput[]) => { - const formattedCameras = cameras.map((camera, index) => ({ - //eslint-disable-next-line i18next/no-literal-string - element: `${index + 1}. ${camera.label}`, + const formattedCameras = cameras.map((camera) => ({ + title: camera.label, value: camera.id, - id: camera.id, })); setAvailableCameras(formattedCameras); - if (formattedCameras.length > 1) { - // if multiple cameras are available we set first one as active - setActiveCamera(formattedCameras[0]); + const defaultCamera = formattedCameras.at(0); + if (defaultCamera) { + setActiveCamera(defaultCamera.value); setIsLoading(false); } }; @@ -113,7 +111,7 @@ export const QrReaderWrapper = ({ className, onResult, countdown, validationErro error === CameraError.INVALID_ERROR && 'blur-[13px]', className, ), - cameraId: activeCamera?.value, + cameraId: activeCamera, onStart: () => setIsLoading(false), onCameraList, onError, @@ -160,16 +158,20 @@ export const QrReaderWrapper = ({ className, onResult, countdown, validationErro )} -
- {availableCameras && availableCameras.length > 1 && ( +
+ {availableCameras.length > 1 && ( )}
diff --git a/src/renderer/features/currency/CurrencyForm/ui/CurrencyForm.tsx b/src/renderer/features/currency/CurrencyForm/ui/CurrencyForm.tsx index 0ad2713bb9..f316a9d636 100644 --- a/src/renderer/features/currency/CurrencyForm/ui/CurrencyForm.tsx +++ b/src/renderer/features/currency/CurrencyForm/ui/CurrencyForm.tsx @@ -4,20 +4,18 @@ import { type FormEvent, useEffect } from 'react'; import { type CurrencyItem } from '@/shared/api/price-provider'; import { useI18n } from '@/shared/i18n'; -import { Button, FootnoteText, HelpText, Select, Switch } from '@/shared/ui'; -import { type DropdownOption } from '@/shared/ui/types'; +import { nonNullable } from '@/shared/lib/utils'; +import { Button, FootnoteText, HelpText, Switch } from '@/shared/ui'; +import { Select } from '@/shared/ui-kit'; import { type Callbacks, currencyFormModel } from '../model/currency-form'; -const getCurrencyOption = (currency: CurrencyItem): DropdownOption => ({ - id: currency.id.toString(), - value: currency, - element: [currency.code, currency.symbol, currency.name].filter(Boolean).join(' • '), -}); +const getCurrencyTitle = (currency: CurrencyItem): string => { + return [currency.code, currency.symbol, currency.name].filter(nonNullable).join(' • '); +}; type Props = Callbacks; export const CurrencyForm = ({ onSubmit }: Props) => { const { t } = useI18n(); - const isFormValid = useUnit(currencyFormModel.$isFormValid); useEffect(() => { currencyFormModel.events.callbacksChanged({ onSubmit }); @@ -32,34 +30,11 @@ export const CurrencyForm = ({ onSubmit }: Props) => { fields: { fiatFlag, currency }, } = useForm(currencyFormModel.$currencyForm); + const isFormValid = useUnit(currencyFormModel.$isFormValid); const cryptoCurrencies = useUnit(currencyFormModel.$cryptoCurrencies); const popularFiatCurrencies = useUnit(currencyFormModel.$popularFiatCurrencies); const unpopularFiatCurrencies = useUnit(currencyFormModel.$unpopularFiatCurrencies); - const currenciesOptions: DropdownOption[] = [ - { - id: 'crypto', - element: {t('settings.currency.cryptocurrenciesLabel')}, - value: {} as CurrencyItem, - disabled: true, - }, - ...cryptoCurrencies.map(getCurrencyOption), - { - id: 'popular', - element: {t('settings.currency.popularFiatLabel')}, - value: {} as CurrencyItem, - disabled: true, - }, - ...popularFiatCurrencies.map(getCurrencyOption), - { - id: 'unpopular', - element: {t('settings.currency.unpopularFiatLabel')}, - value: {} as CurrencyItem, - disabled: true, - }, - ...unpopularFiatCurrencies.map(getCurrencyOption), - ]; - const submitForm = (event: FormEvent) => { event.preventDefault(); submit(); @@ -76,11 +51,32 @@ export const CurrencyForm = ({ onSubmit }: Props) => {
diff --git a/src/renderer/pages/Onboarding/Vault/KeyQrReader/KeyQrReader.tsx b/src/renderer/pages/Onboarding/Vault/KeyQrReader/KeyQrReader.tsx index a0d39cad38..2a97c0fad6 100644 --- a/src/renderer/pages/Onboarding/Vault/KeyQrReader/KeyQrReader.tsx +++ b/src/renderer/pages/Onboarding/Vault/KeyQrReader/KeyQrReader.tsx @@ -54,8 +54,9 @@ const KeyQrReader = ({ size = 300, className, onResult }: Props) => { setAvailableCameras(formattedCameras); - if (formattedCameras.length > 0) { - setActiveCamera(formattedCameras[0].value); + const defaultCamera = formattedCameras.at(0); + if (defaultCamera) { + setActiveCamera(defaultCamera.value); setCameraState(CameraState.ACTIVE); } }; diff --git a/src/renderer/shared/ui-kit/Select/Select.stories.tsx b/src/renderer/shared/ui-kit/Select/Select.stories.tsx index 115c1c60d0..4e71ea9778 100644 --- a/src/renderer/shared/ui-kit/Select/Select.stories.tsx +++ b/src/renderer/shared/ui-kit/Select/Select.stories.tsx @@ -9,9 +9,6 @@ import { Select } from './Select'; const meta: Meta = { title: 'Design System/kit/Select', component: Select, - parameters: { - layout: 'centered', - }, render: (params) => { const [value, onChange] = useState(''); @@ -83,6 +80,27 @@ export const Disabled: Story = { }, }; +export const Groups: Story = { + render: (params) => { + const [value, onChange] = useState(''); + + return ( + + + + ); + }, +}; + export const Dark: Story = { decorators: [ (Story, { args }) => { diff --git a/src/renderer/shared/ui-kit/Select/Select.tsx b/src/renderer/shared/ui-kit/Select/Select.tsx index 5b73388eb2..42d39d65f1 100644 --- a/src/renderer/shared/ui-kit/Select/Select.tsx +++ b/src/renderer/shared/ui-kit/Select/Select.tsx @@ -1,5 +1,5 @@ import * as RadixSelect from '@radix-ui/react-select'; -import { type PropsWithChildren, createContext, useContext, useMemo } from 'react'; +import { Children, type PropsWithChildren, type ReactNode, createContext, useContext, useMemo } from 'react'; import { type XOR } from '@/shared/core'; import { cnTw } from '@/shared/lib/utils'; @@ -130,6 +130,22 @@ const Content = ({ children }: PropsWithChildren) => { ); }; +type GroupProps = { + title: ReactNode; +}; +const Group = ({ title, children }: PropsWithChildren) => { + if (Children.count(children) === 0) return null; + + return ( + + +
{title}
+
+ {children} +
+ ); +}; + type ItemProps = { value: string; }; @@ -156,5 +172,6 @@ const Item = ({ value, children }: PropsWithChildren) => { }; export const Select = Object.assign(Root, { + Group, Item, });