Skip to content

Commit

Permalink
refactor: labels
Browse files Browse the repository at this point in the history
  • Loading branch information
ankormoreankor committed Feb 7, 2025
1 parent e596ef3 commit f09f752
Show file tree
Hide file tree
Showing 11 changed files with 228 additions and 248 deletions.
4 changes: 2 additions & 2 deletions packages/ui/locales/en/views.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"pull-requests": "Pull Requests",
"branches": "Branches",
"settings": "Settings",
"descriptionPlaceholder": "Enter a description",
"descriptionPlaceholder": "Enter a short description for the label",
"description": "Description",
"error": "Error:",
"saving": "Saving…",
Expand Down Expand Up @@ -442,4 +442,4 @@
"edit": "Edit webhook",
"delete": "Delete webhook"
}
}
}
4 changes: 2 additions & 2 deletions packages/ui/locales/es/views.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"pull-requests": "Pull Requests",
"branches": "Branches",
"settings": "Settings",
"descriptionPlaceholder": "Enter a description",
"descriptionPlaceholder": "Enter a short description for the label",
"description": "Description",
"error": "Error:",
"saving": "Saving...",
Expand Down Expand Up @@ -434,4 +434,4 @@
"token": "Token",
"status": "Status"
}
}
}
3 changes: 1 addition & 2 deletions packages/ui/locales/fr/views.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"pull-requests": "Requêtes de tirage",
"branches": "Branches",
"settings": "Paramètres",
"descriptionPlaceholder": "Entrez une description",
"description": "Description",
"error": "Erreur :",
"saving": "Enregistrement...",
Expand Down Expand Up @@ -436,4 +435,4 @@
"edit": "Edit webhook",
"delete": "Delete webhook"
}
}
}
23 changes: 7 additions & 16 deletions packages/ui/src/components/input.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { forwardRef, Fragment, InputHTMLAttributes, ReactNode } from 'react'
import { forwardRef, InputHTMLAttributes, ReactNode } from 'react'

import { Caption, ControlGroup, Icon, IconProps, Label, Message, MessageTheme } from '@/components'
import { cn } from '@utils/cn'
Expand All @@ -9,12 +9,12 @@ export interface BaseInputProps
VariantProps<typeof inputVariants> {}

const inputVariants = cva(
'bg-input-background px-3 py-1 text-foreground-1 disabled:cursor-not-allowed disabled:bg-background-3 disabled:text-foreground-7',
'bg-input-background text-foreground-1 disabled:bg-background-3 disabled:text-foreground-7 px-3 py-1 disabled:cursor-not-allowed',
{
variants: {
variant: {
default:
'flex w-full rounded border text-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-foreground-4 focus-visible:rounded focus-visible:outline-none',
'placeholder:text-foreground-4 flex w-full rounded border text-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:rounded focus-visible:outline-none',
extended: 'grow border-none focus-visible:outline-none'
},
size: {
Expand Down Expand Up @@ -69,7 +69,7 @@ const BaseInputWithWrapper = forwardRef<HTMLInputElement, BaseInputWithWrapperPr

BaseInputWithWrapper.displayName = 'BaseInputWithWrapper'

interface InputProps extends BaseInputProps {
export interface InputProps extends BaseInputProps {
label?: string
caption?: ReactNode
error?: string
Expand Down Expand Up @@ -114,14 +114,6 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
},
ref
) => {
const isControlGroup = !!error || !!caption || !!label || !!wrapperClassName
const InputWrapper = isControlGroup ? ControlGroup : Fragment
const inputWrapperProps = isControlGroup
? {
className: wrapperClassName
}
: {}

const InputComponent = customContent ? BaseInputWithWrapper : BaseInput

const baseInputComp = (
Expand Down Expand Up @@ -158,7 +150,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(

return inputIconName ? (
<span className="relative">
<Icon className="absolute left-3 top-1/2 -translate-y-1/2 text-icons-9" name={inputIconName} size={14} />
<Icon className="text-icons-9 absolute left-3 top-1/2 -translate-y-1/2" name={inputIconName} size={14} />
{baseInputComp}
</span>
) : (
Expand All @@ -167,7 +159,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
}

return (
<InputWrapper {...inputWrapperProps}>
<ControlGroup className={wrapperClassName}>
{!!label && (
<Label className="mb-2.5" color={disabled ? 'disabled-dark' : 'secondary'} optional={optional} htmlFor={id}>
{label}
Expand All @@ -182,12 +174,11 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
</Message>
)}
{caption && <Caption className={disabled ? 'text-foreground-9' : ''}>{caption}</Caption>}
</InputWrapper>
</ControlGroup>
)
}
)

Input.displayName = 'Input'

export { Input }
export type { InputProps }
46 changes: 26 additions & 20 deletions packages/ui/src/components/label-marker.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { FC } from 'react'

import { Icon } from '@/components'
import { cn } from '@utils/cn'
import { ColorsEnum } from '@views/labels'
import { ColorsEnum, ILabelType, LabelType } from '@views/labels'

export interface LabelMarkerProps {
export interface LabelMarkerProps extends Pick<ILabelType, 'color' | 'type'> {
label: string
value?: string
color: ColorsEnum
inTable?: boolean
}

export const LabelMarkerColor = {
Expand All @@ -26,22 +25,29 @@ export const LabelMarkerColor = {
[ColorsEnum.LIME]: 'bg-label-background-lime text-label-foreground-lime'
}

export const LabelMarker: FC<LabelMarkerProps> = ({ label, value, color, inTable = false }) => {
export const LabelMarker: FC<LabelMarkerProps> = ({ label, value, color, type }) => {
const isDynamic = type === LabelType.DYNAMIC

return (
<span
className={cn(
`inline-flex text-12 leading-5 h-5 py-px px-2 items-center font-medium rounded overflow-hidden max-w-full ${LabelMarkerColor[color]}`,
{ 'pr-px': !!value }
)}
>
<span className={cn('truncate', { 'flex-1': inTable }, { 'pr-1.5': !!value })}>{label}</span>
{!!value && (
<span
className={`flex h-full items-center overflow-hidden rounded-r bg-label-background-black pl-1.5 pr-[7px] ${inTable && 'max-w-[50%]'}`}
>
<span className="max-w-full truncate">{value}</span>
</span>
)}
</span>
<div className="flex max-w-full items-center gap-2">
<span
className={cn(
`inline-flex text-12 leading-3 h-5 py-px px-2 items-center font-medium rounded overflow-hidden w-fit ${LabelMarkerColor[color]}`,
{ 'pr-px': !!value }
)}
>
<span className={cn('truncate', { 'pr-1.5': !!value })}>{label}</span>

{!!value && (
<span
className={`bg-label-background-black flex h-full items-center overflow-hidden rounded-[3px] pl-1.5 pr-[7px]`}
>
<span className="max-w-full truncate">{value}</span>
</span>
)}
</span>

{isDynamic && <Icon name="circle-plus" size={12} className="text-icons-4" />}
</div>
)
}
6 changes: 3 additions & 3 deletions packages/ui/src/components/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useDebounceSearch } from '@hooks/use-debounce-search'
import * as SelectPrimitive from '@radix-ui/react-select'
import { cn } from '@utils/cn'

interface SelectProps
export interface SelectProps
extends Omit<Omit<PropsWithChildren<React.HTMLAttributes<HTMLElement>>, 'defaultValue'>, 'dir'>,
SelectPrimitive.SelectProps {
label?: string
Expand Down Expand Up @@ -141,7 +141,7 @@ const SelectContent = forwardRef<
)}
>
{!!withSearch && (
<div className="sticky -top-1 z-[1] -mx-1 -mt-1 mb-1 border-b bg-background-2 px-3 py-2.5">
<div className="bg-background-2 sticky -top-1 z-[1] -mx-1 -mt-1 mb-1 border-b px-3 py-2.5">
<SearchBox.Root
className="w-full"
placeholder={searchProps?.placeholder || ''}
Expand All @@ -155,7 +155,7 @@ const SelectContent = forwardRef<
children
) : (
<div className="px-5 py-4 text-center">
<span className="leading-tight text-foreground-2">Nothing to select</span>
<span className="text-foreground-2 leading-tight">Nothing to select</span>
</div>
)}
</SelectPrimitive.Viewport>
Expand Down
35 changes: 21 additions & 14 deletions packages/ui/src/views/labels/components/label-cell-content.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,52 @@
import { FC } from 'react'

import { Accordion, Icon, LabelMarker } from '@/components'
import { ILabelType, LabelValuesType } from '@/views'
import { ILabelType, LabelType, LabelValueType } from '@/views'
import { cn } from '@utils/cn'

export interface LabelCellContentProps {
label: ILabelType
values: LabelValuesType
values?: LabelValueType[]
}

export const LabelCellContent: FC<LabelCellContentProps> = ({ label, values }) => {
const value = values?.[label.key]
const length = value ? value.length : 0
const isWithValues = values?.length && values.length > 0

return (
<Accordion.Root collapsible type="single">
<Accordion.Item value={label.key} isLast disabled={!length}>
<Accordion.Item value={label.key} isLast disabled={!isWithValues}>
<Accordion.Trigger className="max-w-full flex-auto cursor-pointer p-0" hideChevron>
<div className={cn('flex gap-x-2.5 items-center max-w-full', { 'pl-[22px]': !length })}>
{!!length && (
<div className={cn('flex gap-x-2.5 items-center max-w-full', { 'pl-[22px]': !isWithValues })}>
{isWithValues && (
<Icon
className="flex-none text-icons-1 transition-all group-hover:text-icons-2 group-data-[state=open]:rotate-180 group-data-[state=open]:text-icons-2"
className="text-icons-1 group-hover:text-icons-2 group-data-[state=open]:text-icons-2 flex-none transition-all group-data-[state=open]:rotate-180"
name="arrow-short"
size={12}
/>
)}
<LabelMarker inTable label={label.key} color={label.color} value={length ? String(length) : undefined} />

<LabelMarker
label={label.key}
color={label.color}
value={isWithValues ? String(values.length) : undefined}
type={label.type}
/>
</div>
</Accordion.Trigger>
<Accordion.Content className="flex flex-col gap-y-2.5 pb-0 pl-[22px] pt-2.5">
{!!value &&
value.map(item => (

{isWithValues && (
<Accordion.Content className="flex flex-col gap-y-2.5 pb-0 pl-[22px] pt-2.5">
{values.map(item => (
<LabelMarker
key={item.id}
inTable
label={label.key}
color={item?.color || label.color}
value={item.value}
type={LabelType.STATIC}
/>
))}
</Accordion.Content>
</Accordion.Content>
)}
</Accordion.Item>
</Accordion.Root>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { FC } from 'react'
import { UseFormRegister } from 'react-hook-form'

import { Button, Icon, Input, Select, SelectContent, SelectItem } from '@/components'
import { ColorsEnum, CreateLabelFormFields, TranslationStore } from '@/views'
import { Button, Icon, Input, InputProps, Select, SelectContent, SelectItem, SelectProps } from '@/components'
import { cn, ColorsEnum, TranslationStore } from '@/views'

const SelectColorMarker = {
[ColorsEnum.RED]: 'bg-label-foreground-red',
Expand All @@ -21,62 +20,60 @@ const SelectColorMarker = {
}

interface LabelFormColorAndNameGroupProps {
name: string
colorValue: ColorsEnum
handleColorChange: (color: ColorsEnum) => void
colorError?: string
nameError?: string
className?: string
isValue?: boolean
useTranslationStore: () => TranslationStore
register: UseFormRegister<CreateLabelFormFields>
registerName: keyof CreateLabelFormFields
handleDeleteValue?: () => void
selectProps?: SelectProps
inputProps?: InputProps
}

export const LabelFormColorAndNameGroup: FC<LabelFormColorAndNameGroupProps> = ({
name,
colorValue,
handleColorChange,
colorError,
nameError,
className,
isValue = false,
useTranslationStore,
register,
registerName,
handleDeleteValue
handleDeleteValue,
selectProps,
inputProps
}) => {
const { t } = useTranslationStore()

const isWithDeleteButton = isValue && !!handleDeleteValue

return (
<div className="flex gap-x-2.5">
<div className="w-32 flex-none">
<Select name={`color-${name}`} value={colorValue} onValueChange={handleColorChange} error={colorError}>
<SelectContent>
{Object.values(ColorsEnum).map(color => (
<SelectItem key={color} value={color}>
<div className="flex max-w-full items-center gap-x-1.5">
<div className={`size-2 rounded-full ${SelectColorMarker[color]}`} />
<span className="truncate">{color}</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div
className={cn(
'grid grid-cols-[8rem_1fr] gap-x-2.5',
isWithDeleteButton && 'grid-cols-[8rem_1fr_auto]',
className
)}
>
<Select {...selectProps}>
<SelectContent>
{Object.values(ColorsEnum).map(color => (
<SelectItem key={color} value={color}>
<div className="flex max-w-full items-center gap-x-1.5">
<div className={`size-2 min-h-2 min-w-2 rounded-full ${SelectColorMarker[color]}`} />
<span className="text-foreground-3 truncate">{color}</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>

<Input
wrapperClassName="flex-1"
{...register(registerName)}
placeholder={
isValue
? t('views:labelData.form.colorValuePlaceholder', 'Enter value name')
: t('views:labelData.form.colorLabelPlaceholder', 'Enter label name')
}
error={nameError}
size="md"
{...inputProps}
/>
{isValue && !!handleDeleteValue && (

{isWithDeleteButton && (
<Button
className="mx-[-9px] h-9 flex-none text-icons-1 hover:text-icons-2"
className="text-icons-1 hover:text-icons-2 mx-[-9px] h-9 flex-none"
variant="custom"
size="icon"
onClick={handleDeleteValue}
Expand Down
Loading

0 comments on commit f09f752

Please sign in to comment.