Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor(ui): replace label elements with custom Label component
- Replace raw label elements with the new Label component across multiple UI components
- Add consistent styling through variant and size props
- Remove manual className combinations in favor of component variants
- Maintain accessibility attributes and functionality
  • Loading branch information
olegshulyakov committed Oct 9, 2025
commit 58ed5a903f7923d6c351f9ae6badfe3b9aef54df
14 changes: 7 additions & 7 deletions src/components/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ import SpeechToText, {
SpeechRecordCallback,
} from '../hooks/useSpeechToText';
import { MessageExtra } from '../types';
import { classNames, cleanCurrentUrl } from '../utils';
import { cleanCurrentUrl } from '../utils';
import { DropzoneArea } from './DropzoneArea';
import { Label } from './ui/label';
import { Textarea } from './ui/textarea';

/**
Expand Down Expand Up @@ -139,18 +140,17 @@ export const ChatInput = memo(
{/* buttons area */}
<div className="flex items-center justify-between mt-2">
<div className="flex gap-2 items-center">
<label
<Label
className={isPending ? 'btn-disabled' : ''}
variant="btn-ghost"
size="icon-rounded"
htmlFor="new-message-file-upload"
className={classNames({
'btn btn-ghost w-8 h-8 p-0 rounded-full': true,
'btn-disabled': isPending,
})}
aria-label={t('chatInput.ariaLabels.uploadFile')}
tabIndex={0}
role="button"
>
<LuPaperclip className="lucide h-5 w-5" />
</label>
</Label>

<Button
className="xl:hidden"
Expand Down
8 changes: 5 additions & 3 deletions src/components/ChatMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
import ChatInputExtraContextItem from './ChatInputExtraContextItem';
import { DropzoneArea } from './DropzoneArea';
import MarkdownDisplay from './MarkdownDisplay';
import { Label } from './ui/label';
import { Textarea } from './ui/textarea';

interface SplitMessage {
Expand Down Expand Up @@ -436,15 +437,16 @@ function EditMessage({
<div className="flex flex-row mt-2">
{msg.role === 'user' && (
<>
<label
<Label
variant="btn-ghost"
size="icon-rounded"
htmlFor={`file-upload-${msg.id}`}
className="btn w-8 h-8 mt-1 p-0 rounded-full"
aria-label={t('chatScreen.ariaLabels.uploadFile')}
tabIndex={0}
role="button"
>
<LuPaperclip className="lucide h-5 w-5" />
</label>
</Label>
<div className="grow" />
</>
)}
Expand Down
16 changes: 9 additions & 7 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useAppContext } from '../context/app';
import { useChatContext } from '../context/chat';
import { useInferenceContext } from '../context/inference';
import { Dropdown } from './common';
import { Label } from './ui/label';

export default function Header() {
const navigate = useNavigate();
Expand Down Expand Up @@ -40,13 +41,14 @@ export default function Header() {
<header className="flex flex-col gap-2 justify-center max-md:pb-2 md:py-2 sticky top-0 z-10">
<section className="flex flex-row items-center xl:hidden">
{/* open sidebar button */}
<label htmlFor="toggle-drawer" className="btn btn-ghost w-8 h-8 p-0">
<Label variant="btn-ghost" size="icon" htmlFor="toggle-drawer">
<LuMenu className="lucide h-5 w-5" />
</label>
</Label>

{/* spacer */}
<label
className="grow font-medium truncate text-center cursor-pointer px-4"
<Label
variant="fake-btn"
className="grow font-medium truncate px-4"
aria-label={title}
role="button"
onClick={() => {
Expand All @@ -56,7 +58,7 @@ export default function Header() {
}}
>
{title}
</label>
</Label>

{/* new conversation button */}
<Button
Expand All @@ -72,12 +74,12 @@ export default function Header() {

{showSettings && (
<section className="flex items-center max-xl:hidden">
<label
<Label
className="font-medium truncate text-center px-4"
aria-label={title}
>
{title}
</label>
</Label>
</section>
)}

Expand Down
28 changes: 17 additions & 11 deletions src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import IndexedDB from '../database/indexedDB';
import { Conversation } from '../types';
import { classNames } from '../utils';
import { downloadAsFile } from './common';
import { Label } from './ui/label';

export default function Sidebar() {
const navigate = useNavigate();
Expand Down Expand Up @@ -67,26 +68,29 @@ export default function Sidebar() {
<div className="flex flex-col bg-base-300 h-full min-h-0 max-w-full xl:w-72 pb-4 px-4 xl:pl-2 xl:pr-0">
<div className="flex flex-row items-center justify-between xl:py-2">
{/* close sidebar button */}
<label className="w-8 h-8 p-0 max-xl:hidden"></label>
<label
<Label size="icon" className="max-xl:hidden" />
<Label
className="xl:hidden"
variant="btn-ghost"
size="icon-rounded"
htmlFor="toggle-drawer"
className="btn btn-ghost w-8 h-8 p-0 rounded-full xl:hidden"
role="button"
title={t('sidebar.buttons.closeSideBar')}
aria-label={t('sidebar.buttons.closeSideBar')}
role="button"
tabIndex={0}
>
<LuX className="lucide w-5 h-5" />
</label>
</Label>

<label
className="font-bold tracking-wider leading-8 text-center cursor-pointer"
<Label
variant="fake-btn"
className="font-bold tracking-wider leading-8"
aria-label={import.meta.env.VITE_APP_NAME}
role="button"
onClick={() => navigate('/')}
>
{import.meta.env.VITE_APP_NAME}
</label>
</Label>

{/* new conversation button */}
<Button
Expand Down Expand Up @@ -135,8 +139,10 @@ const ConversationGroup = memo(
<div role="group">
{/* group name (by date) */}
{/* we use btn class here to make sure that the padding/margin are aligned with the other items */}
<b
className="btn btn-ghost btn-xs bg-none btn-disabled block text-xs text-base-content text-start px-2 mb-0 mt-6 font-bold opacity-75"
<Label
className="px-2 mb-0 mt-6 opacity-75"
variant="group-title"
size="xs"
role="note"
aria-description={t(`sidebar.groups.${group.title}`, {
defaultValue: group.title,
Expand All @@ -147,7 +153,7 @@ const ConversationGroup = memo(
i18nKey={`sidebar.groups.${group.title}`}
defaults={group.title}
/>
</b>
</Label>

<ul>
{group.conversations.map((conv) => (
Expand Down
7 changes: 4 additions & 3 deletions src/components/settings/ImportExportComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useAppContext } from '../../context/app';
import { normalizeUrl } from '../../utils';
import { downloadAsFile } from '../common';
import { Button } from '../ui/button';
import { Label } from '../ui/label';

export function ImportExportComponent({ onClose }: { onClose: () => void }) {
const { t } = useTranslation();
Expand Down Expand Up @@ -55,16 +56,16 @@ export function ImportExportComponent({ onClose }: { onClose: () => void }) {
onInput={onImport}
hidden
/>
<label
<Label
variant="btn"
htmlFor="file-import"
className="btn"
aria-label={t('settings.importExport.importBtnLabel')}
tabIndex={0}
role="button"
>
<TbDatabaseImport className="lucide w-4 h-4 mr-1 inline" />
{t('settings.importExport.importBtnLabel')}
</label>
</Label>
</div>

<DelimeterComponent />
Expand Down
9 changes: 5 additions & 4 deletions src/components/settings/ThemeController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Trans } from 'react-i18next';
import { SYNTAX_THEMES, THEMES } from '../../config';
import { useAppContext } from '../../context/app';
import { Dropdown, DropdownOption } from '../common';
import { Label } from '../ui/label';

export function ThemeController() {
const dataThemes = ['auto', ...THEMES].map((theme) => ({
Expand Down Expand Up @@ -57,7 +58,7 @@ export function ThemeController() {
<div className="font-bold mb-1 md:hidden">
<Trans i18nKey="settings.themeManager.dataTheme.label" />
</div>
<label className="input input-bordered join-item grow flex items-center gap-2 mb-1">
<Label variant="input-bordered">
<div className="font-bold hidden md:block">
<Trans i18nKey="settings.themeManager.dataTheme.label" />
</div>
Expand All @@ -71,7 +72,7 @@ export function ThemeController() {
isSelected={(option) => currentTheme === option.value}
onSelect={(option) => switchTheme(option.value)}
/>
</label>
</Label>
<div className="text-xs opacity-75 max-w-80">
<Trans i18nKey="settings.themeManager.dataTheme.note" />
</div>
Expand All @@ -82,7 +83,7 @@ export function ThemeController() {
<div className="font-bold mb-1 md:hidden">
<Trans i18nKey="settings.themeManager.syntaxTheme.label" />
</div>
<label className="input input-bordered join-item grow flex items-center gap-2 mb-1">
<Label variant="input-bordered">
<div className="font-bold hidden md:block">
<Trans i18nKey="settings.themeManager.syntaxTheme.label" />
</div>
Expand All @@ -96,7 +97,7 @@ export function ThemeController() {
isSelected={(option) => currentSyntaxTheme === option.value}
onSelect={(option) => switchSyntaxTheme(option.value)}
/>
</label>
</Label>
<div className="text-xs opacity-75 max-w-80">
<Trans i18nKey="settings.themeManager.syntaxTheme.note" />
</div>
Expand Down
25 changes: 13 additions & 12 deletions src/components/settings/common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { CONFIG_DEFAULT } from '../../config';
import { DropdownOption, SettingFieldInput } from '../../types/settings';
import { classNames, normalizeUrl } from '../../utils';
import { Dropdown } from '../common';
import { Label } from '../ui/label';
import { Textarea } from '../ui/textarea';

interface BaseInputProps {
Expand Down Expand Up @@ -47,7 +48,7 @@ export function SettingsModalLongInput({
return (
<LabeledField configKey={field.translateKey || field.key}>
{({ label, note }) => (
<label className="form-control flex flex-col justify-center max-w-80 mb-3">
<Label variant="form-control" className="max-w-80">
<div className="text-sm opacity-60 mb-1">{label}</div>
<Textarea
size="default"
Expand All @@ -62,7 +63,7 @@ export function SettingsModalLongInput({
dangerouslySetInnerHTML={{ __html: note }}
/>
)}
</label>
</Label>
)}
</LabeledField>
);
Expand All @@ -76,11 +77,11 @@ export function SettingsModalShortInput({
return (
<LabeledField configKey={field.translateKey || field.key}>
{({ label, note }) => (
<label className="form-control flex flex-col justify-center mb-3">
<Label variant="form-control">
<div tabIndex={0} role="button" className="font-bold mb-1 md:hidden">
{label}
</div>
<label className="input input-bordered join-item grow flex items-center gap-2 mb-1">
<Label variant="input-bordered">
<div
tabIndex={0}
role="button"
Expand All @@ -96,14 +97,14 @@ export function SettingsModalShortInput({
onChange={(e) => onChange(e.target.value)}
disabled={field.disabled}
/>
</label>
</Label>
{note && (
<div
className="text-xs opacity-75 max-w-80"
dangerouslySetInnerHTML={{ __html: note }}
/>
)}
</label>
</Label>
)}
</LabeledField>
);
Expand Down Expand Up @@ -135,11 +136,11 @@ export function SettingsModalRangeInput({
return (
<LabeledField configKey={field.translateKey || field.key}>
{({ label, note }) => (
<label className="form-control flex flex-col justify-center mb-3">
<Label variant="form-control">
<div tabIndex={0} role="button" className="font-bold mb-1 md:hidden">
{label}
</div>
<label className="input input-bordered join-item grow flex items-center gap-2 mb-1">
<Label variant="input-bordered">
<div
tabIndex={0}
role="button"
Expand All @@ -164,14 +165,14 @@ export function SettingsModalRangeInput({
))}
</div>
</div>
</label>
</Label>
{note && (
<div
className="text-xs opacity-75 max-w-80"
dangerouslySetInnerHTML={{ __html: note }}
/>
)}
</label>
</Label>
)}
</LabeledField>
);
Expand All @@ -185,7 +186,7 @@ export function SettingsModalCheckbox({
return (
<LabeledField configKey={field.translateKey || field.key}>
{({ label, note }) => (
<label className="form-control flex flex-col justify-center mb-3">
<Label variant="form-control">
<div className="flex flex-row items-center mb-1">
<input
type="checkbox"
Expand All @@ -202,7 +203,7 @@ export function SettingsModalCheckbox({
dangerouslySetInnerHTML={{ __html: note }}
/>
)}
</label>
</Label>
)}
</LabeledField>
);
Expand Down
Loading