From 2ce6ac74f41589923695dde53bba8c533a18a4a4 Mon Sep 17 00:00:00 2001 From: Marco Beretta <81851188+berry-13@users.noreply.github.com> Date: Mon, 28 Jul 2025 18:18:59 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=BB=20feat:=20radio=20component=20(#86?= =?UTF-8?q?92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 📦 feat: Add Radio component * 📦 feat: Integrate localization for 'No options available' message in Radio component * 📦 feat: Bump version to 0.2.0 in package.json * 📦 feat: Update client package version to 0.2.0 in package-lock.json --- package-lock.json | 2 +- packages/client/package.json | 2 +- packages/client/src/components/Radio.tsx | 99 +++++++++++++++++++ packages/client/src/components/index.ts | 1 + .../client/src/locales/en/translation.json | 3 +- 5 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 packages/client/src/components/Radio.tsx diff --git a/package-lock.json b/package-lock.json index 9cef1c6644..e89438099b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51506,7 +51506,7 @@ }, "packages/client": { "name": "@librechat/client", - "version": "0.1.9", + "version": "0.2.0", "devDependencies": { "@rollup/plugin-alias": "^5.1.0", "@rollup/plugin-commonjs": "^25.0.2", diff --git a/packages/client/package.json b/packages/client/package.json index 5edd6505cf..f1775ed13d 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@librechat/client", - "version": "0.1.9", + "version": "0.2.0", "description": "React components for LibreChat", "main": "dist/index.js", "module": "dist/index.es.js", diff --git a/packages/client/src/components/Radio.tsx b/packages/client/src/components/Radio.tsx new file mode 100644 index 0000000000..b419f78e6d --- /dev/null +++ b/packages/client/src/components/Radio.tsx @@ -0,0 +1,99 @@ +import React, { useState, useRef, useLayoutEffect, useCallback, memo } from 'react'; +import { useLocalize } from '~/hooks'; + +interface Option { + value: string; + label: string; +} + +interface RadioProps { + options: Option[]; + value?: string; + onChange?: (value: string) => void; + disabled?: boolean; +} + +const Radio = memo(function Radio({ options, value, onChange, disabled = false }: RadioProps) { + const localize = useLocalize(); + const [currentValue, setCurrentValue] = useState(value ?? ''); + const buttonRefs = useRef<(HTMLButtonElement | null)[]>([]); + const [backgroundStyle, setBackgroundStyle] = useState({}); + + const handleChange = (newValue: string) => { + setCurrentValue(newValue); + onChange?.(newValue); + }; + + const updateBackgroundStyle = useCallback(() => { + const selectedIndex = options.findIndex((opt) => opt.value === currentValue); + if (selectedIndex >= 0 && buttonRefs.current[selectedIndex]) { + const selectedButton = buttonRefs.current[selectedIndex]; + const container = selectedButton?.parentElement; + if (selectedButton && container) { + const containerRect = container.getBoundingClientRect(); + const buttonRect = selectedButton.getBoundingClientRect(); + const offsetLeft = buttonRect.left - containerRect.left - 4; + setBackgroundStyle({ + width: `${buttonRect.width}px`, + transform: `translateX(${offsetLeft}px)`, + }); + } + } + }, [currentValue, options]); + + useLayoutEffect(() => { + updateBackgroundStyle(); + }, [updateBackgroundStyle]); + + useLayoutEffect(() => { + if (value !== undefined) { + setCurrentValue(value); + } + }, [value]); + + if (options.length === 0) { + return ( +
+ + {localize('com_ui_no_options')} + +
+ ); + } + + const selectedIndex = options.findIndex((opt) => opt.value === currentValue); + + return ( +
+ {selectedIndex >= 0 && ( +
+ )} + {options.map((option, index) => ( + + ))} +
+ ); +}); + +export default Radio; diff --git a/packages/client/src/components/index.ts b/packages/client/src/components/index.ts index edaf240dc4..0269a04550 100644 --- a/packages/client/src/components/index.ts +++ b/packages/client/src/components/index.ts @@ -30,6 +30,7 @@ export * from './Progress'; export * from './InputOTP'; export * from './MultiSearch'; export * from './Resizable'; +export { default as Radio } from './Radio'; export { default as Badge } from './Badge'; export { default as Combobox } from './Combobox'; export { default as Dropdown } from './Dropdown'; diff --git a/packages/client/src/locales/en/translation.json b/packages/client/src/locales/en/translation.json index de39a30059..9913398ff6 100644 --- a/packages/client/src/locales/en/translation.json +++ b/packages/client/src/locales/en/translation.json @@ -1,3 +1,4 @@ { - "com_ui_cancel": "Cancel" + "com_ui_cancel": "Cancel", + "com_ui_no_options": "No options available" }