torch-glare 2.1.7 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,9 +33,9 @@ import { useState } from 'react'
33
33
 
34
34
  export function BasicBadgeField() {
35
35
  const [tags] = useState([
36
- { id: '1', name: 'React', variant: 'blue' },
37
- { id: '2', name: 'TypeScript', variant: 'purple' },
38
- { id: '3', name: 'Next.js', variant: 'slate' },
36
+ { id: '1', name: 'React', isSelected: false, variant: 'blue' },
37
+ { id: '2', name: 'TypeScript', isSelected: false, variant: 'purple' },
38
+ { id: '3', name: 'Next.js', isSelected: false, variant: 'slate' },
39
39
  ])
40
40
 
41
41
  return (
@@ -59,16 +59,16 @@ export function TechnologySelector() {
59
59
  const [selectedTags, setSelectedTags] = useState<Tag[]>([])
60
60
 
61
61
  const allTags: Tag[] = [
62
- { id: '1', name: 'React', variant: 'blue' },
63
- { id: '2', name: 'TypeScript', variant: 'purple' },
64
- { id: '3', name: 'Next.js', variant: 'slate' },
65
- { id: '4', name: 'Tailwind CSS', variant: 'green' },
66
- { id: '5', name: 'Node.js', variant: 'green' },
67
- { id: '6', name: 'PostgreSQL', variant: 'blue' },
68
- { id: '7', name: 'MongoDB', variant: 'green' },
69
- { id: '8', name: 'GraphQL', variant: 'purple' },
70
- { id: '9', name: 'REST API', variant: 'gray' },
71
- { id: '10', name: 'Docker', variant: 'blue' },
62
+ { id: '1', name: 'React', isSelected: false, variant: 'blue' },
63
+ { id: '2', name: 'TypeScript', isSelected: false, variant: 'purple' },
64
+ { id: '3', name: 'Next.js', isSelected: false, variant: 'slate' },
65
+ { id: '4', name: 'Tailwind CSS', isSelected: false, variant: 'green' },
66
+ { id: '5', name: 'Node.js', isSelected: false, variant: 'green' },
67
+ { id: '6', name: 'PostgreSQL', isSelected: false, variant: 'blue' },
68
+ { id: '7', name: 'MongoDB', isSelected: false, variant: 'green' },
69
+ { id: '8', name: 'GraphQL', isSelected: false, variant: 'purple' },
70
+ { id: '9', name: 'REST API', isSelected: false, variant: 'gray' },
71
+ { id: '10', name: 'Docker', isSelected: false, variant: 'blue' },
72
72
  ]
73
73
 
74
74
  return (
@@ -101,13 +101,13 @@ Filter content by multiple categories.
101
101
  ```tsx
102
102
  export function CategoryFilter() {
103
103
  const categories: Tag[] = [
104
- { id: 'design', name: 'Design', variant: 'purple' },
105
- { id: 'development', name: 'Development', variant: 'blue' },
106
- { id: 'marketing', name: 'Marketing', variant: 'yellow' },
107
- { id: 'sales', name: 'Sales', variant: 'green' },
108
- { id: 'support', name: 'Support', variant: 'orange' },
109
- { id: 'hr', name: 'Human Resources', variant: 'rose' },
110
- { id: 'finance', name: 'Finance', variant: 'slate' },
104
+ { id: 'design', name: 'Design', isSelected: false, variant: 'purple' },
105
+ { id: 'development', name: 'Development', isSelected: false, variant: 'blue' },
106
+ { id: 'marketing', name: 'Marketing', isSelected: false, variant: 'yellow' },
107
+ { id: 'sales', name: 'Sales', isSelected: false, variant: 'green' },
108
+ { id: 'support', name: 'Support', isSelected: false, variant: 'orange' },
109
+ { id: 'hr', name: 'Human Resources', isSelected: false, variant: 'rose' },
110
+ { id: 'finance', name: 'Finance', isSelected: false, variant: 'slate' },
111
111
  ]
112
112
 
113
113
  const [selectedCategories, setSelectedCategories] = useState<Tag[]>([])
@@ -148,21 +148,21 @@ Multi-select skills for job applications or profiles.
148
148
  ```tsx
149
149
  export function SkillsSelector() {
150
150
  const skills: Tag[] = [
151
- { id: 'js', name: 'JavaScript', variant: 'yellow' },
152
- { id: 'ts', name: 'TypeScript', variant: 'blue' },
153
- { id: 'react', name: 'React', variant: 'blue' },
154
- { id: 'vue', name: 'Vue.js', variant: 'green' },
155
- { id: 'angular', name: 'Angular', variant: 'red' },
156
- { id: 'node', name: 'Node.js', variant: 'green' },
157
- { id: 'python', name: 'Python', variant: 'blue' },
158
- { id: 'java', name: 'Java', variant: 'orange' },
159
- { id: 'go', name: 'Go', variant: 'green' },
160
- { id: 'rust', name: 'Rust', variant: 'orange' },
161
- { id: 'sql', name: 'SQL', variant: 'slate' },
162
- { id: 'nosql', name: 'NoSQL', variant: 'green' },
163
- { id: 'aws', name: 'AWS', variant: 'yellow' },
164
- { id: 'azure', name: 'Azure', variant: 'blue' },
165
- { id: 'gcp', name: 'Google Cloud', variant: 'blue' },
151
+ { id: 'js', name: 'JavaScript', isSelected: false, variant: 'yellow' },
152
+ { id: 'ts', name: 'TypeScript', isSelected: false, variant: 'blue' },
153
+ { id: 'react', name: 'React', isSelected: false, variant: 'blue' },
154
+ { id: 'vue', name: 'Vue.js', isSelected: false, variant: 'green' },
155
+ { id: 'angular', name: 'Angular', isSelected: false, variant: 'red' },
156
+ { id: 'node', name: 'Node.js', isSelected: false, variant: 'green' },
157
+ { id: 'python', name: 'Python', isSelected: false, variant: 'blue' },
158
+ { id: 'java', name: 'Java', isSelected: false, variant: 'orange' },
159
+ { id: 'go', name: 'Go', isSelected: false, variant: 'green' },
160
+ { id: 'rust', name: 'Rust', isSelected: false, variant: 'orange' },
161
+ { id: 'sql', name: 'SQL', isSelected: false, variant: 'slate' },
162
+ { id: 'nosql', name: 'NoSQL', isSelected: false, variant: 'green' },
163
+ { id: 'aws', name: 'AWS', isSelected: false, variant: 'yellow' },
164
+ { id: 'azure', name: 'Azure', isSelected: false, variant: 'blue' },
165
+ { id: 'gcp', name: 'Google Cloud', isSelected: false, variant: 'blue' },
166
166
  ]
167
167
 
168
168
  const [selectedSkills, setSelectedSkills] = useState<Tag[]>([])
@@ -213,16 +213,16 @@ Tag management for projects with custom variants.
213
213
  ```tsx
214
214
  export function ProjectTags() {
215
215
  const projectTags: Tag[] = [
216
- { id: '1', name: 'High Priority', variant: 'red' },
217
- { id: '2', name: 'In Progress', variant: 'blue' },
218
- { id: '3', name: 'Completed', variant: 'green' },
219
- { id: '4', name: 'On Hold', variant: 'yellow' },
220
- { id: '5', name: 'Needs Review', variant: 'purple' },
221
- { id: '6', name: 'Client Approval', variant: 'purple' },
222
- { id: '7', name: 'Internal', variant: 'gray' },
223
- { id: '8', name: 'External', variant: 'slate' },
224
- { id: '9', name: 'Urgent', variant: 'orange' },
225
- { id: '10', name: 'Can Wait', variant: 'green' },
216
+ { id: '1', name: 'High Priority', isSelected: false, variant: 'red' },
217
+ { id: '2', name: 'In Progress', isSelected: false, variant: 'blue' },
218
+ { id: '3', name: 'Completed', isSelected: false, variant: 'green' },
219
+ { id: '4', name: 'On Hold', isSelected: false, variant: 'yellow' },
220
+ { id: '5', name: 'Needs Review', isSelected: false, variant: 'purple' },
221
+ { id: '6', name: 'Client Approval', isSelected: false, variant: 'purple' },
222
+ { id: '7', name: 'Internal', isSelected: false, variant: 'gray' },
223
+ { id: '8', name: 'External', isSelected: false, variant: 'slate' },
224
+ { id: '9', name: 'Urgent', isSelected: false, variant: 'orange' },
225
+ { id: '10', name: 'Can Wait', isSelected: false, variant: 'green' },
226
226
  ]
227
227
 
228
228
  const [projectData, setProjectData] = useState({
@@ -275,9 +275,9 @@ BadgeField in various sizes.
275
275
  ```tsx
276
276
  export function BadgeFieldSizes() {
277
277
  const tags: Tag[] = [
278
- { id: '1', name: 'Small', variant: 'blue' },
279
- { id: '2', name: 'Medium', variant: 'green' },
280
- { id: '3', name: 'Large', variant: 'purple' },
278
+ { id: '1', name: 'Small', isSelected: false, variant: 'blue' },
279
+ { id: '2', name: 'Medium', isSelected: false, variant: 'green' },
280
+ { id: '3', name: 'Large', isSelected: false, variant: 'purple' },
281
281
  ]
282
282
 
283
283
  return (
@@ -308,9 +308,9 @@ Badge field with custom icons and action buttons.
308
308
  ```tsx
309
309
  export function BadgeFieldWithIcon() {
310
310
  const tags: Tag[] = [
311
- { id: '1', name: 'JavaScript', variant: 'yellow' },
312
- { id: '2', name: 'Python', variant: 'blue' },
313
- { id: '3', name: 'Ruby', variant: 'red' },
311
+ { id: '1', name: 'JavaScript', isSelected: false, variant: 'yellow' },
312
+ { id: '2', name: 'Python', isSelected: false, variant: 'blue' },
313
+ { id: '3', name: 'Ruby', isSelected: false, variant: 'red' },
314
314
  ]
315
315
 
316
316
  return (
@@ -339,9 +339,9 @@ export function BadgeFieldWithError() {
339
339
  const [error, setError] = useState('')
340
340
 
341
341
  const availableTags: Tag[] = [
342
- { id: '1', name: 'Option 1', variant: 'blue' },
343
- { id: '2', name: 'Option 2', variant: 'green' },
344
- { id: '3', name: 'Option 3', variant: 'purple' },
342
+ { id: '1', name: 'Option 1', isSelected: false, variant: 'blue' },
343
+ { id: '2', name: 'Option 2', isSelected: false, variant: 'green' },
344
+ { id: '3', name: 'Option 3', isSelected: false, variant: 'purple' },
345
345
  ]
346
346
 
347
347
  const handleChange = (newTags: Tag[]) => {
@@ -382,10 +382,10 @@ Tag-style email recipient selector.
382
382
  ```tsx
383
383
  export function EmailRecipients() {
384
384
  const contacts: Tag[] = [
385
- { id: '1', name: 'john@example.com', variant: 'blue' },
386
- { id: '2', name: 'jane@example.com', variant: 'green' },
387
- { id: '3', name: 'team@example.com', variant: 'purple' },
388
- { id: '4', name: 'support@example.com', variant: 'slate' },
385
+ { id: '1', name: 'john@example.com', isSelected: false, variant: 'blue' },
386
+ { id: '2', name: 'jane@example.com', isSelected: false, variant: 'green' },
387
+ { id: '3', name: 'team@example.com', isSelected: false, variant: 'purple' },
388
+ { id: '4', name: 'support@example.com', isSelected: false, variant: 'slate' },
389
389
  ]
390
390
 
391
391
  const [recipients, setRecipients] = useState<Tag[]>([])
@@ -422,37 +422,29 @@ export function EmailRecipients() {
422
422
  }
423
423
  ```
424
424
 
425
- ### Variants
425
+ ### RTL Support
426
426
 
427
- System and Presentation style variants.
427
+ Pass `dir="rtl"` (and a translated `addLabel`) to mirror the field for right-to-left languages.
428
428
 
429
429
  ```tsx
430
- export function BadgeFieldVariants() {
430
+ export function RtlBadgeField() {
431
431
  const tags: Tag[] = [
432
- { id: '1', name: 'Tag 1', variant: 'blue' },
433
- { id: '2', name: 'Tag 2', variant: 'green' },
434
- { id: '3', name: 'Tag 3', variant: 'purple' },
432
+ { id: '1', name: 'إلكترونيات', isSelected: false, variant: 'blue' },
433
+ { id: '2', name: 'كتب', isSelected: false, variant: 'green' },
434
+ { id: '3', name: 'ملابس', isSelected: false, variant: 'purple' },
435
435
  ]
436
436
 
437
- return (
438
- <div className="space-y-6">
439
- <div>
440
- <h3 className="font-semibold mb-2">System Style</h3>
441
- <BadgeField
442
- tags={tags}
443
- variant="SystemStyle"
444
- placeholder="System style variant..."
445
- />
446
- </div>
437
+ const [selected, setSelected] = useState<Tag[]>([])
447
438
 
448
- <div>
449
- <h3 className="font-semibold mb-2">Presentation Style</h3>
450
- <BadgeField
451
- tags={tags}
452
- variant="PresentationStyle"
453
- placeholder="Presentation style variant..."
454
- />
455
- </div>
439
+ return (
440
+ <div dir="rtl">
441
+ <BadgeField
442
+ dir="rtl"
443
+ tags={tags}
444
+ onValueChange={setSelected}
445
+ addLabel="إضافة"
446
+ placeholder="اختر وسماً"
447
+ />
456
448
  </div>
457
449
  )
458
450
  }
@@ -469,7 +461,7 @@ Extends all Input element props (except size and variant).
469
461
  | tags | `Tag[]` | **Required** | Available tags to select from |
470
462
  | onValueChange | `(tags: Tag[]) => void` | - | Callback when selection changes |
471
463
  | size | `'XS' \| 'S' \| 'M'` | `'M'` | Field size |
472
- | variant | `'SystemStyle' \| 'PresentationStyle'` | `'PresentationStyle'` | Visual variant |
464
+ | variant | `'PresentationStyle'` | `'PresentationStyle'` | Visual variant |
473
465
  | icon | `ReactNode` | - | Leading icon |
474
466
  | errorMessage | `string` | - | Error message (shows tooltip) |
475
467
  | onTable | `boolean` | `false` | Table-specific styling |
@@ -478,6 +470,8 @@ Extends all Input element props (except size and variant).
478
470
  | required | `boolean` | `false` | Required indicator |
479
471
  | theme | `Themes` | - | Theme override |
480
472
  | actionButton | `ReactNode` | - | Trailing action button |
473
+ | addLabel | `string` | `'add'` | Label for the add action shown in the field |
474
+ | dir | `string` | `'ltr'` | Reading direction (`'rtl'` for right-to-left) |
481
475
  | placeholder | `string` | - | Input placeholder text |
482
476
 
483
477
  ### Tag Type
@@ -486,7 +480,10 @@ Extends all Input element props (except size and variant).
486
480
  interface Tag {
487
481
  id: string
488
482
  name: string
489
- variant?: BadgeVariant
483
+ isSelected: boolean
484
+ variant?: string
485
+ value?: string
486
+ [key: string]: any
490
487
  }
491
488
  ```
492
489
 
@@ -500,8 +497,8 @@ BadgeField includes comprehensive keyboard support:
500
497
  | `ArrowUp` | Move focus to previous tag in dropdown |
501
498
  | `ArrowLeft` | Move focus to previous selected tag |
502
499
  | `ArrowRight` | Move focus to next selected tag |
503
- | `Enter` | Select focused tag / Remove focused selected tag |
504
- | `Backspace` | Remove last selected tag when input is empty |
500
+ | `Enter` | Select the focused tag from the dropdown |
501
+ | `Delete` / `Backspace` | Remove the focused selected tag |
505
502
  | `Escape` | Close dropdown |
506
503
  | `Tab` | Close dropdown and move to next field |
507
504
 
@@ -531,7 +528,7 @@ BadgeField includes comprehensive keyboard support:
531
528
  interface BadgeFieldProps
532
529
  extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'variant'> {
533
530
  size?: 'XS' | 'S' | 'M'
534
- variant?: 'SystemStyle' | 'PresentationStyle'
531
+ variant?: 'PresentationStyle'
535
532
  icon?: ReactNode
536
533
  errorMessage?: string
537
534
  onTable?: boolean
@@ -542,12 +539,16 @@ interface BadgeFieldProps
542
539
  actionButton?: ReactNode
543
540
  tags: Tag[]
544
541
  onValueChange?: (tags: Tag[]) => void
542
+ addLabel?: string
545
543
  }
546
544
 
547
545
  interface Tag {
548
546
  id: string
549
547
  name: string
550
- variant?: BadgeVariant
548
+ isSelected: boolean
549
+ variant?: string
550
+ value?: string
551
+ [key: string]: any
551
552
  }
552
553
  ```
553
554
 
@@ -596,8 +597,8 @@ import { BadgeField } from '@/components/BadgeField'
596
597
 
597
598
  describe('BadgeField', () => {
598
599
  const mockTags = [
599
- { id: '1', name: 'Tag 1', variant: 'blue' },
600
- { id: '2', name: 'Tag 2', variant: 'green' },
600
+ { id: '1', name: 'Tag 1', isSelected: false, variant: 'blue' },
601
+ { id: '2', name: 'Tag 2', isSelected: false, variant: 'green' },
601
602
  ]
602
603
 
603
604
  it('renders placeholder', () => {
@@ -638,7 +639,10 @@ describe('BadgeField', () => {
638
639
  fireEvent.focus(input)
639
640
  fireEvent.click(screen.getByText('Tag 1'))
640
641
 
641
- expect(handleChange).toHaveBeenCalledWith([mockTags[0]])
642
+ // Selecting a tag emits it with isSelected: true
643
+ expect(handleChange).toHaveBeenCalledWith([
644
+ expect.objectContaining({ id: '1', isSelected: true }),
645
+ ])
642
646
  })
643
647
 
644
648
  it('removes tag on unselect', () => {