@shohojdhara/atomix 0.4.0 → 0.4.2

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.
Files changed (150) hide show
  1. package/dist/atomix.css +0 -14
  2. package/dist/atomix.css.map +1 -1
  3. package/dist/atomix.min.css +4 -4
  4. package/dist/atomix.min.css.map +1 -1
  5. package/dist/charts.d.ts +12 -19
  6. package/dist/charts.js +555 -359
  7. package/dist/charts.js.map +1 -1
  8. package/dist/core.d.ts +98 -28
  9. package/dist/core.js +1082 -733
  10. package/dist/core.js.map +1 -1
  11. package/dist/forms.d.ts +26 -21
  12. package/dist/forms.js +937 -350
  13. package/dist/forms.js.map +1 -1
  14. package/dist/heavy.d.ts +14 -21
  15. package/dist/heavy.js +409 -256
  16. package/dist/heavy.js.map +1 -1
  17. package/dist/index.d.ts +518 -284
  18. package/dist/index.esm.js +1993 -1237
  19. package/dist/index.esm.js.map +1 -1
  20. package/dist/index.js +1994 -1237
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.min.js +1 -1
  23. package/dist/index.min.js.map +1 -1
  24. package/package.json +2 -2
  25. package/scripts/atomix-cli.js +43 -1
  26. package/scripts/cli/__tests__/utils.test.js +6 -2
  27. package/scripts/cli/migration-tools.js +2 -2
  28. package/scripts/cli/theme-bridge.js +7 -9
  29. package/scripts/cli/utils.js +2 -1
  30. package/src/components/Accordion/Accordion.stories.tsx +40 -0
  31. package/src/components/Accordion/Accordion.tsx +174 -56
  32. package/src/components/Accordion/AccordionCompound.test.tsx +70 -0
  33. package/src/components/AtomixGlass/AtomixGlass.tsx +82 -54
  34. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +17 -18
  35. package/src/components/AtomixGlass/README.md +5 -5
  36. package/src/components/AtomixGlass/stories/Customization.stories.tsx +2 -2
  37. package/src/components/AtomixGlass/stories/Examples.stories.tsx +42 -42
  38. package/src/components/AtomixGlass/stories/Modes.stories.tsx +5 -5
  39. package/src/components/AtomixGlass/stories/Overview.stories.tsx +3 -3
  40. package/src/components/AtomixGlass/stories/Performance.stories.tsx +2 -2
  41. package/src/components/AtomixGlass/stories/Playground.stories.tsx +45 -45
  42. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +3 -3
  43. package/src/components/Badge/Badge.stories.tsx +1 -1
  44. package/src/components/Badge/Badge.tsx +1 -1
  45. package/src/components/Breadcrumb/Breadcrumb.tsx +185 -65
  46. package/src/components/Breadcrumb/BreadcrumbCompound.test.tsx +84 -0
  47. package/src/components/Breadcrumb/index.ts +2 -2
  48. package/src/components/Button/Button.stories.tsx +1 -1
  49. package/src/components/Button/README.md +2 -2
  50. package/src/components/Callout/Callout.stories.tsx +166 -1011
  51. package/src/components/Callout/Callout.test.tsx +3 -3
  52. package/src/components/Callout/Callout.tsx +196 -84
  53. package/src/components/Callout/CalloutCompound.test.tsx +72 -0
  54. package/src/components/Callout/README.md +2 -2
  55. package/src/components/Chart/Chart.stories.tsx +1 -1
  56. package/src/components/Chart/Chart.tsx +5 -5
  57. package/src/components/Chart/TreemapChart.tsx +37 -29
  58. package/src/components/DatePicker/readme.md +3 -3
  59. package/src/components/Dropdown/Dropdown.stories.tsx +1 -1
  60. package/src/components/Dropdown/Dropdown.tsx +133 -20
  61. package/src/components/Dropdown/DropdownCompound.test.tsx +64 -0
  62. package/src/components/EdgePanel/EdgePanel.stories.tsx +7 -7
  63. package/src/components/EdgePanel/EdgePanel.tsx +164 -112
  64. package/src/components/EdgePanel/EdgePanelCompound.test.tsx +53 -0
  65. package/src/components/Form/Checkbox.stories.tsx +1 -1
  66. package/src/components/Form/Checkbox.tsx +1 -1
  67. package/src/components/Form/Input.stories.tsx +1 -1
  68. package/src/components/Form/Input.tsx +1 -1
  69. package/src/components/Form/Radio.stories.tsx +1 -1
  70. package/src/components/Form/Radio.tsx +1 -1
  71. package/src/components/Form/Select.stories.tsx +24 -1
  72. package/src/components/Form/Select.test.tsx +99 -0
  73. package/src/components/Form/Select.tsx +145 -94
  74. package/src/components/Form/SelectOption.tsx +88 -0
  75. package/src/components/Form/Textarea.stories.tsx +1 -1
  76. package/src/components/Form/Textarea.tsx +1 -1
  77. package/src/components/Hero/Hero.stories.tsx +39 -2
  78. package/src/components/Hero/Hero.test.tsx +142 -0
  79. package/src/components/Hero/Hero.tsx +143 -4
  80. package/src/components/List/List.test.tsx +62 -0
  81. package/src/components/List/List.tsx +16 -5
  82. package/src/components/List/ListItem.tsx +20 -0
  83. package/src/components/Messages/Messages.stories.tsx +1 -1
  84. package/src/components/Messages/Messages.tsx +2 -2
  85. package/src/components/Modal/Modal.stories.tsx +66 -2
  86. package/src/components/Modal/Modal.tsx +115 -35
  87. package/src/components/Modal/ModalCompound.test.tsx +94 -0
  88. package/src/components/Navigation/Nav/Nav.stories.tsx +2 -2
  89. package/src/components/Navigation/Nav/Nav.tsx +1 -1
  90. package/src/components/Navigation/Navbar/Navbar.stories.tsx +3 -3
  91. package/src/components/Navigation/Navbar/Navbar.tsx +1 -1
  92. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +2 -2
  93. package/src/components/Navigation/SideMenu/SideMenu.tsx +1 -1
  94. package/src/components/Pagination/Pagination.stories.tsx +1 -1
  95. package/src/components/Pagination/Pagination.tsx +1 -1
  96. package/src/components/Popover/Popover.stories.tsx +1 -1
  97. package/src/components/Popover/Popover.tsx +1 -1
  98. package/src/components/Progress/Progress.tsx +1 -1
  99. package/src/components/Rating/Rating.stories.tsx +1 -1
  100. package/src/components/Rating/Rating.test.tsx +73 -0
  101. package/src/components/Rating/Rating.tsx +25 -37
  102. package/src/components/Spinner/Spinner.tsx +1 -1
  103. package/src/components/Steps/Steps.stories.tsx +1 -1
  104. package/src/components/Steps/Steps.tsx +125 -22
  105. package/src/components/Steps/StepsCompound.test.tsx +81 -0
  106. package/src/components/Tabs/Tabs.stories.tsx +1 -1
  107. package/src/components/Tabs/Tabs.tsx +198 -45
  108. package/src/components/Tabs/TabsCompound.test.tsx +64 -0
  109. package/src/components/Todo/Todo.tsx +0 -1
  110. package/src/components/Toggle/Toggle.stories.tsx +1 -1
  111. package/src/components/Toggle/Toggle.tsx +1 -1
  112. package/src/components/Tooltip/Tooltip.stories.tsx +1 -1
  113. package/src/components/VideoPlayer/VideoPlayer.stories.tsx +2 -2
  114. package/src/lib/composables/__tests__/useAtomixGlassPerf.test.tsx +88 -0
  115. package/src/lib/composables/__tests__/useChart.test.ts +50 -0
  116. package/src/lib/composables/__tests__/useChart.test.tsx +139 -0
  117. package/src/lib/composables/__tests__/useHeroBackgroundSlider.test.tsx +59 -0
  118. package/src/lib/composables/__tests__/useSliderAutoplay.test.tsx +68 -0
  119. package/src/lib/composables/atomix-glass/useGlassBackgroundDetection.ts +329 -0
  120. package/src/lib/composables/atomix-glass/useGlassCornerRadius.ts +82 -0
  121. package/src/lib/composables/atomix-glass/useGlassMouseTracking.ts +153 -0
  122. package/src/lib/composables/atomix-glass/useGlassOverLight.ts +198 -0
  123. package/src/lib/composables/atomix-glass/useGlassSize.ts +117 -0
  124. package/src/lib/composables/atomix-glass/useGlassState.ts +112 -0
  125. package/src/lib/composables/atomix-glass/useGlassTransforms.ts +160 -0
  126. package/src/lib/composables/glass-styles.ts +302 -0
  127. package/src/lib/composables/index.ts +0 -8
  128. package/src/lib/composables/useAtomixGlass.ts +331 -537
  129. package/src/lib/composables/useAtomixGlassStyles.ts +307 -0
  130. package/src/lib/composables/useBarChart.ts +1 -1
  131. package/src/lib/composables/useBreadcrumb.ts +6 -6
  132. package/src/lib/composables/useChart.ts +104 -21
  133. package/src/lib/composables/useHeroBackgroundSlider.ts +16 -7
  134. package/src/lib/composables/useSlider.ts +66 -34
  135. package/src/lib/theme/devtools/CLI.ts +2 -10
  136. package/src/lib/theme/utils/__tests__/themeUtils.test.ts +213 -0
  137. package/src/lib/types/components.ts +21 -23
  138. package/src/lib/utils/__tests__/componentUtils.test.ts +57 -2
  139. package/src/lib/utils/__tests__/dom.test.ts +100 -0
  140. package/src/lib/utils/__tests__/fontPreloader.test.ts +102 -0
  141. package/src/lib/utils/__tests__/themeNaming.test.ts +117 -0
  142. package/src/lib/utils/themeNaming.ts +1 -1
  143. package/src/styles/06-components/_components.accordion.scss +0 -2
  144. package/src/styles/06-components/_components.chart.scss +0 -1
  145. package/src/styles/06-components/_components.dropdown.scss +0 -1
  146. package/src/styles/06-components/_components.edge-panel.scss +0 -2
  147. package/src/styles/06-components/_components.photoviewer.scss +0 -1
  148. package/src/styles/06-components/_components.river.scss +0 -1
  149. package/src/styles/06-components/_components.slider.scss +0 -3
  150. package/src/styles/99-utilities/_utilities.glass-fixes.scss +0 -1
@@ -66,7 +66,7 @@ const meta: Meta<typeof AtomixGlass> = {
66
66
  description: 'Elasticity factor for mouse interactions (default: 0.15)',
67
67
  table: { defaultValue: { summary: '0.15' } },
68
68
  },
69
- cornerRadius: {
69
+ borderRadius: {
70
70
  control: { type: 'range', min: 0, max: 50, step: 1 },
71
71
  description: 'Corner radius in pixels (default: 20)',
72
72
  table: { defaultValue: { summary: '20' } },
@@ -137,12 +137,12 @@ const meta: Meta<typeof AtomixGlass> = {
137
137
  description: 'Override for high contrast preference (default: false)',
138
138
  table: { defaultValue: { summary: 'false' } },
139
139
  },
140
- disableEffects: {
140
+ withoutEffects: {
141
141
  control: 'boolean',
142
142
  description: 'Disable all visual effects (default: false)',
143
143
  table: { defaultValue: { summary: 'false' } },
144
144
  },
145
- enablePerformanceMonitoring: {
145
+ debugPerformance: {
146
146
  control: 'boolean',
147
147
  description: 'Enable performance monitoring (default: false)',
148
148
  table: { defaultValue: { summary: 'false' } },
@@ -299,13 +299,13 @@ export const Playground: Story = {
299
299
  saturation: 140,
300
300
  aberrationIntensity: 2,
301
301
  elasticity: 0.15,
302
- cornerRadius: 20,
302
+ borderRadius: 20,
303
303
  overLight: false,
304
304
  reducedMotion: false,
305
305
  highContrast: false,
306
- disableEffects: false,
307
- enableLiquidBlur: false,
308
- enableBorderEffect: true,
306
+ withoutEffects: false,
307
+ withLiquidBlur: false,
308
+ withBorder: true,
309
309
  });
310
310
 
311
311
  // eslint-disable-next-line react-hooks/rules-of-hooks
@@ -333,13 +333,13 @@ export const Playground: Story = {
333
333
  saturation: 110,
334
334
  aberrationIntensity: 0.5,
335
335
  elasticity: 0.05,
336
- cornerRadius: 12,
336
+ borderRadius: 12,
337
337
  overLight: false,
338
338
  reducedMotion: false,
339
339
  highContrast: false,
340
- disableEffects: false,
341
- enableLiquidBlur: false,
342
- enableBorderEffect: true,
340
+ withoutEffects: false,
341
+ withLiquidBlur: false,
342
+ withBorder: true,
343
343
  },
344
344
  mode: 'standard' as const,
345
345
  shader: 'liquidGlass' as const,
@@ -353,13 +353,13 @@ export const Playground: Story = {
353
353
  saturation: 140,
354
354
  aberrationIntensity: 2,
355
355
  elasticity: 0.15,
356
- cornerRadius: 20,
356
+ borderRadius: 20,
357
357
  overLight: false,
358
358
  reducedMotion: false,
359
359
  highContrast: false,
360
- disableEffects: false,
361
- enableLiquidBlur: false,
362
- enableBorderEffect: true,
360
+ withoutEffects: false,
361
+ withLiquidBlur: false,
362
+ withBorder: true,
363
363
  },
364
364
  mode: 'standard' as const,
365
365
  shader: 'liquidGlass' as const,
@@ -373,13 +373,13 @@ export const Playground: Story = {
373
373
  saturation: 170,
374
374
  aberrationIntensity: 3.5,
375
375
  elasticity: 0.25,
376
- cornerRadius: 28,
376
+ borderRadius: 28,
377
377
  overLight: false,
378
378
  reducedMotion: false,
379
379
  highContrast: false,
380
- disableEffects: false,
381
- enableLiquidBlur: true,
382
- enableBorderEffect: true,
380
+ withoutEffects: false,
381
+ withLiquidBlur: true,
382
+ withBorder: true,
383
383
  },
384
384
  mode: 'prominent' as const,
385
385
  shader: 'plasma' as const,
@@ -393,13 +393,13 @@ export const Playground: Story = {
393
393
  saturation: 200,
394
394
  aberrationIntensity: 5,
395
395
  elasticity: 0.35,
396
- cornerRadius: 32,
396
+ borderRadius: 32,
397
397
  overLight: false,
398
398
  reducedMotion: false,
399
399
  highContrast: false,
400
- disableEffects: false,
401
- enableLiquidBlur: true,
402
- enableBorderEffect: true,
400
+ withoutEffects: false,
401
+ withLiquidBlur: true,
402
+ withBorder: true,
403
403
  },
404
404
  mode: 'shader' as const,
405
405
  shader: 'waves' as const,
@@ -420,15 +420,15 @@ export const Playground: Story = {
420
420
  saturation={${settings.saturation}}
421
421
  aberrationIntensity={${settings.aberrationIntensity}}
422
422
  elasticity={${settings.elasticity}}
423
- cornerRadius={${settings.cornerRadius}}
423
+ borderRadius={${settings.borderRadius}}
424
424
  overLight={${settings.overLight}}
425
425
  mode="${selectedMode}"
426
426
  shaderVariant="${selectedShader}"
427
427
  reducedMotion={${settings.reducedMotion}}
428
428
  highContrast={${settings.highContrast}}
429
- disableEffects={${settings.disableEffects}}
430
- enableLiquidBlur={${settings.enableLiquidBlur}}
431
- enableBorderEffect={${settings.enableBorderEffect}}
429
+ withoutEffects={${settings.withoutEffects}}
430
+ withLiquidBlur={${settings.withLiquidBlur}}
431
+ withBorder={${settings.withBorder}}
432
432
  >
433
433
  <div className="your-content">
434
434
  {/* Your content here */}
@@ -729,7 +729,7 @@ export const Playground: Story = {
729
729
  ? 300
730
730
  : key === 'aberrationIntensity'
731
731
  ? 10
732
- : key === 'cornerRadius'
732
+ : key === 'borderRadius'
733
733
  ? 100
734
734
  : key === 'blurAmount'
735
735
  ? 10
@@ -872,13 +872,13 @@ export const Playground: Story = {
872
872
  saturation: 140,
873
873
  aberrationIntensity: 2,
874
874
  elasticity: 0.15,
875
- cornerRadius: 20,
875
+ borderRadius: 20,
876
876
  overLight: false,
877
877
  reducedMotion: false,
878
878
  highContrast: false,
879
- disableEffects: false,
880
- enableLiquidBlur: false,
881
- enableBorderEffect: true,
879
+ withoutEffects: false,
880
+ withLiquidBlur: false,
881
+ withBorder: true,
882
882
  });
883
883
  setSelectedMode('standard');
884
884
  setSelectedShader('liquidGlass');
@@ -906,7 +906,7 @@ export const Playground: Story = {
906
906
  <AtomixGlass
907
907
  displacementScale={80}
908
908
  aberrationIntensity={1}
909
- cornerRadius={16}
909
+ borderRadius={16}
910
910
  saturation={120}
911
911
  >
912
912
  <div style={{ padding: '2.5rem' }}>
@@ -980,15 +980,15 @@ export const Playground: Story = {
980
980
  saturation={settings.saturation}
981
981
  aberrationIntensity={settings.aberrationIntensity}
982
982
  elasticity={settings.elasticity}
983
- cornerRadius={settings.cornerRadius}
983
+ borderRadius={settings.borderRadius}
984
984
  overLight={settings.overLight}
985
985
  mode={selectedMode}
986
986
  shaderVariant={selectedShader as any}
987
987
  reducedMotion={settings.reducedMotion}
988
988
  highContrast={settings.highContrast}
989
- disableEffects={settings.disableEffects}
990
- enableLiquidBlur={settings.enableLiquidBlur}
991
- enableBorderEffect={settings.enableBorderEffect}
989
+ withoutEffects={settings.withoutEffects}
990
+ withLiquidBlur={settings.withLiquidBlur}
991
+ withBorder={settings.withBorder}
992
992
  style={{ width: '100%' }}
993
993
  >
994
994
  <div style={{ padding: '2.5rem', textAlign: 'center' }}>
@@ -1150,31 +1150,31 @@ export const Playground: Story = {
1150
1150
  style={{
1151
1151
  padding: '4px 12px',
1152
1152
  borderRadius: '12px',
1153
- background: settings.enableLiquidBlur
1153
+ background: settings.withLiquidBlur
1154
1154
  ? 'rgba(122, 255, 215, 0.2)'
1155
1155
  : 'rgba(255,255,255,0.1)',
1156
1156
  fontSize: '0.75rem',
1157
- border: settings.enableLiquidBlur
1157
+ border: settings.withLiquidBlur
1158
1158
  ? '1px solid #7AFFD7'
1159
1159
  : '1px solid transparent',
1160
1160
  }}
1161
1161
  >
1162
- {settings.enableLiquidBlur ? '✓' : '○'} Liquid Blur
1162
+ {settings.withLiquidBlur ? '✓' : '○'} Liquid Blur
1163
1163
  </div>
1164
1164
  <div
1165
1165
  style={{
1166
1166
  padding: '4px 12px',
1167
1167
  borderRadius: '12px',
1168
- background: settings.enableBorderEffect
1168
+ background: settings.withBorder
1169
1169
  ? 'rgba(122, 255, 215, 0.2)'
1170
1170
  : 'rgba(255,255,255,0.1)',
1171
1171
  fontSize: '0.75rem',
1172
- border: settings.enableBorderEffect
1172
+ border: settings.withBorder
1173
1173
  ? '1px solid #7AFFD7'
1174
1174
  : '1px solid transparent',
1175
1175
  }}
1176
1176
  >
1177
- {settings.enableBorderEffect ? '✓' : '○'} Border Effect
1177
+ {settings.withBorder ? '✓' : '○'} Border Effect
1178
1178
  </div>
1179
1179
  <div
1180
1180
  style={{
@@ -1238,7 +1238,7 @@ export const Playground: Story = {
1238
1238
  <div>
1239
1239
  <span className="u-opacity-70">Radius:</span>
1240
1240
  <span className="u-font-semibold u-ml-2">
1241
- {settings.cornerRadius}px
1241
+ {settings.borderRadius}px
1242
1242
  </span>
1243
1243
  </div>
1244
1244
  <div>
@@ -1250,7 +1250,7 @@ export const Playground: Story = {
1250
1250
  <div>
1251
1251
  <span className="u-opacity-70">Effects:</span>
1252
1252
  <span className="u-font-semibold u-ml-2">
1253
- {settings.disableEffects ? 'Disabled' : 'Enabled'}
1253
+ {settings.withoutEffects ? 'Disabled' : 'Enabled'}
1254
1254
  </span>
1255
1255
  </div>
1256
1256
  </div>
@@ -125,7 +125,7 @@ export const LiquidGlass: Story = {
125
125
  saturation: 150,
126
126
  aberrationIntensity: 2,
127
127
  elasticity: 0.2,
128
- cornerRadius: 32,
128
+ borderRadius: 32,
129
129
  mode: 'shader',
130
130
  shaderVariant: 'liquidGlass',
131
131
  },
@@ -246,7 +246,7 @@ export const AppleFluid: Story = {
246
246
  saturation: 150,
247
247
  aberrationIntensity: 2,
248
248
  elasticity: 0.2,
249
- cornerRadius: 32,
249
+ borderRadius: 32,
250
250
  mode: 'shader',
251
251
  shaderVariant: 'appleFluid',
252
252
  },
@@ -367,7 +367,7 @@ export const PremiumGlass: Story = {
367
367
  saturation: 150,
368
368
  aberrationIntensity: 2,
369
369
  elasticity: 0.2,
370
- cornerRadius: 32,
370
+ borderRadius: 32,
371
371
  mode: 'shader',
372
372
  shaderVariant: 'premiumGlass',
373
373
  },
@@ -389,7 +389,7 @@ export const WithCustomGlassSettings: Story = {
389
389
  blurAmount: 2,
390
390
  saturation: 200,
391
391
  aberrationIntensity: 1,
392
- cornerRadius: 16,
392
+ borderRadius: 16,
393
393
  mode: 'polar',
394
394
  },
395
395
  },
@@ -60,7 +60,7 @@ export const Badge: React.FC<BadgeProps> = memo(
60
60
  // Default glass settings for badges
61
61
  const defaultGlassProps = {
62
62
  displacementScale: 20,
63
- cornerRadius: ref.current?.getBoundingClientRect().width
63
+ borderRadius: ref.current?.getBoundingClientRect().width
64
64
  ? ref.current?.getBoundingClientRect().width / 2
65
65
  : 16,
66
66
  className: 'c-badge--glass',
@@ -1,7 +1,16 @@
1
- import React, { ReactNode, memo } from 'react';
1
+ import React, {
2
+ ReactNode,
3
+ memo,
4
+ forwardRef,
5
+ Children,
6
+ cloneElement,
7
+ isValidElement,
8
+ ElementType,
9
+ } from 'react';
2
10
  import { BREADCRUMB } from '../../lib/constants/components';
3
11
 
4
- export interface BreadcrumbItem {
12
+ // Legacy Item Interface
13
+ export interface BreadcrumbItemData {
5
14
  /**
6
15
  * Text to display
7
16
  */
@@ -38,11 +47,110 @@ export interface BreadcrumbItem {
38
47
  className?: string;
39
48
  }
40
49
 
50
+ // Rename exported type to avoid conflict with the component constant
51
+ export type BreadcrumbItemType = BreadcrumbItemData;
52
+
53
+ // Compound Component Props
54
+ export interface BreadcrumbItemProps extends React.HTMLAttributes<HTMLLIElement> {
55
+ /**
56
+ * URL for the breadcrumb item
57
+ */
58
+ href?: string;
59
+
60
+ /**
61
+ * Whether this item is active (current page)
62
+ */
63
+ active?: boolean;
64
+
65
+ /**
66
+ * Optional icon to display before the label
67
+ */
68
+ icon?: ReactNode;
69
+
70
+ /**
71
+ * Optional click handler for the link
72
+ */
73
+ onClick?: (event: React.MouseEvent<any>) => void;
74
+
75
+ /**
76
+ * Optional custom link component
77
+ */
78
+ linkAs?: React.ElementType<any>;
79
+
80
+ /**
81
+ * Link props to pass to the underlying anchor or LinkComponent
82
+ */
83
+ linkProps?: Record<string, any>;
84
+ }
85
+
86
+ export const BreadcrumbItem = forwardRef<HTMLLIElement, BreadcrumbItemProps>(
87
+ (
88
+ {
89
+ children,
90
+ href,
91
+ active,
92
+ icon,
93
+ onClick,
94
+ className = '',
95
+ style,
96
+ linkAs,
97
+ linkProps = {},
98
+ ...props
99
+ },
100
+ ref
101
+ ) => {
102
+ const itemClasses = [
103
+ BREADCRUMB.CLASSES.ITEM,
104
+ active ? BREADCRUMB.CLASSES.ACTIVE : '',
105
+ className,
106
+ ]
107
+ .filter(Boolean)
108
+ .join(' ');
109
+
110
+ const linkContent = (
111
+ <>
112
+ {icon && <span className="c-breadcrumb__icon">{icon}</span>}
113
+ {children}
114
+ </>
115
+ );
116
+
117
+ const commonLinkProps = {
118
+ className: BREADCRUMB.CLASSES.LINK,
119
+ onClick: onClick as any,
120
+ style, // Apply style to the link as per legacy behavior
121
+ ...linkProps,
122
+ };
123
+
124
+ const LinkComponent = linkAs;
125
+
126
+ return (
127
+ <li ref={ref} className={itemClasses} style={style} {...props}>
128
+ {href && !active ? (
129
+ LinkComponent ? (
130
+ // @ts-ignore - Dynamic components are tricky in TS without stricter types
131
+ <LinkComponent href={href} {...commonLinkProps}>
132
+ {linkContent}
133
+ </LinkComponent>
134
+ ) : (
135
+ <a href={href} {...(commonLinkProps as React.HTMLAttributes<HTMLAnchorElement>)}>
136
+ {linkContent}
137
+ </a>
138
+ )
139
+ ) : (
140
+ <span className={BREADCRUMB.CLASSES.LINK}>{linkContent}</span>
141
+ )}
142
+ </li>
143
+ );
144
+ }
145
+ );
146
+
147
+ BreadcrumbItem.displayName = 'BreadcrumbItem';
148
+
41
149
  export interface BreadcrumbProps {
42
150
  /**
43
- * Array of breadcrumb items
151
+ * Array of breadcrumb items (Legacy)
44
152
  */
45
- items: BreadcrumbItem[];
153
+ items?: BreadcrumbItemData[];
46
154
 
47
155
  /**
48
156
  * Custom divider character or element
@@ -68,71 +176,83 @@ export interface BreadcrumbProps {
68
176
  * Custom style for the breadcrumb
69
177
  */
70
178
  style?: React.CSSProperties;
179
+
180
+ /**
181
+ * Children (Compound)
182
+ */
183
+ children?: ReactNode;
71
184
  }
72
- export const Breadcrumb: React.FC<BreadcrumbProps> = memo(
73
- ({
74
- items,
75
- divider,
76
- className = '',
77
- 'aria-label': ariaLabel = 'Breadcrumb',
78
- LinkComponent,
79
- style,
80
- }) => {
81
- const breadcrumbClasses = [BREADCRUMB.CLASSES.BASE, className].filter(Boolean).join(' ');
82
185
 
83
- return (
84
- <nav aria-label={ariaLabel} style={style}>
85
- <ol className={breadcrumbClasses}>
86
- {items.map((item, index) => {
87
- const isLast = index === items.length - 1;
88
- const itemClasses = [
89
- BREADCRUMB.CLASSES.ITEM,
90
- item.active || isLast ? BREADCRUMB.CLASSES.ACTIVE : '',
91
- ]
92
- .filter(Boolean)
93
- .join(' ');
94
-
95
- const linkContent = (
96
- <>
97
- {item.icon && <span className="c-breadcrumb__icon">{item.icon}</span>}
98
- {item.label}
99
- </>
100
- );
101
-
102
- const linkProps = {
103
- href: item.href,
104
- className: BREADCRUMB.CLASSES.LINK,
105
- onClick: item.onClick,
106
- style: item.style,
107
- };
108
-
109
- return (
110
- <li key={index} className={itemClasses} style={item.style}>
111
- {item.href && !item.active ? (
112
- LinkComponent ? (
113
- (() => {
114
- const Component = LinkComponent as React.ComponentType<any>;
115
- return (
116
- <Component {...(linkProps as React.ComponentProps<React.ElementType>)}>
117
- {linkContent}
118
- </Component>
119
- );
120
- })()
121
- ) : (
122
- <a {...(linkProps as React.ComponentProps<'a'>)}>{linkContent}</a>
123
- )
124
- ) : (
125
- <span className={BREADCRUMB.CLASSES.LINK}>{linkContent}</span>
126
- )}
127
- </li>
128
- );
129
- })}
130
- </ol>
131
- </nav>
132
- );
186
+ const BreadcrumbComponent: React.FC<BreadcrumbProps> = memo(function BreadcrumbBase({
187
+ items,
188
+ divider,
189
+ className = '',
190
+ 'aria-label': ariaLabel = 'Breadcrumb',
191
+ LinkComponent,
192
+ style,
193
+ children,
194
+ }: BreadcrumbProps) {
195
+ const breadcrumbClasses = [BREADCRUMB.CLASSES.BASE, className].filter(Boolean).join(' ');
196
+
197
+ let content: ReactNode;
198
+
199
+ if (items && items.length > 0) {
200
+ // Legacy rendering
201
+ content = items.map((item: BreadcrumbItemData, index: number) => {
202
+ const isLast = index === items.length - 1;
203
+
204
+ return (
205
+ <BreadcrumbItem
206
+ key={index}
207
+ href={item.href}
208
+ active={item.active || isLast}
209
+ icon={item.icon}
210
+ onClick={item.onClick as any}
211
+ className={item.className}
212
+ style={item.style}
213
+ linkAs={LinkComponent}
214
+ >
215
+ {item.label}
216
+ </BreadcrumbItem>
217
+ );
218
+ });
219
+ } else {
220
+ // Compound rendering
221
+ const childrenCount = Children.count(children);
222
+ content = Children.map(children, (child, index) => {
223
+ if (isValidElement(child)) {
224
+ const isLast = index === childrenCount - 1;
225
+ const childProps = child.props as any;
226
+
227
+ // Extract props from the child element
228
+ const { active, linkAs, ...otherProps } = childProps;
229
+
230
+ const newProps = {
231
+ active: active ?? (isLast ? true : undefined),
232
+ linkAs: linkAs ?? LinkComponent,
233
+ };
234
+
235
+ return cloneElement(child, newProps as any);
236
+ }
237
+ return child;
238
+ });
133
239
  }
134
- );
240
+
241
+ return (
242
+ <nav aria-label={ariaLabel} style={style}>
243
+ <ol className={breadcrumbClasses}>{content}</ol>
244
+ </nav>
245
+ );
246
+ });
247
+
248
+ export type BreadcrumbType = typeof BreadcrumbComponent & {
249
+ Item: typeof BreadcrumbItem;
250
+ };
251
+
252
+ export const Breadcrumb = BreadcrumbComponent as BreadcrumbType;
135
253
 
136
254
  Breadcrumb.displayName = 'Breadcrumb';
255
+ Breadcrumb.Item = BreadcrumbItem;
137
256
 
138
257
  export default Breadcrumb;
258
+ export type { BreadcrumbItemData as BreadcrumbItemLegacy };
@@ -0,0 +1,84 @@
1
+ import { render, screen, fireEvent } from '@testing-library/react';
2
+ import { describe, it, expect, vi } from 'vitest';
3
+ import { Breadcrumb } from './Breadcrumb';
4
+ import React from 'react';
5
+
6
+ describe('Breadcrumb Component', () => {
7
+ it('renders correctly with legacy items prop', () => {
8
+ const items = [
9
+ { label: 'Home', href: '/' },
10
+ { label: 'Products', href: '/products' },
11
+ { label: 'Current', active: true },
12
+ ];
13
+ render(<Breadcrumb items={items} />);
14
+
15
+ expect(screen.getByText('Home').closest('a')).toHaveAttribute('href', '/');
16
+ expect(screen.getByText('Products').closest('a')).toHaveAttribute('href', '/products');
17
+ expect(screen.getByText('Current').closest('span')).toBeInTheDocument();
18
+ });
19
+
20
+ it('renders correctly with compound components', () => {
21
+ render(
22
+ <Breadcrumb>
23
+ <Breadcrumb.Item href="/">Home</Breadcrumb.Item>
24
+ <Breadcrumb.Item href="/products">Products</Breadcrumb.Item>
25
+ <Breadcrumb.Item active>Current</Breadcrumb.Item>
26
+ </Breadcrumb>
27
+ );
28
+
29
+ expect(screen.getByText('Home').closest('a')).toHaveAttribute('href', '/');
30
+ expect(screen.getByText('Products').closest('a')).toHaveAttribute('href', '/products');
31
+ expect(screen.getByText('Current').closest('span')).toBeInTheDocument();
32
+ });
33
+
34
+ it('handles click events in compound components', () => {
35
+ const handleClick = vi.fn();
36
+ render(
37
+ <Breadcrumb>
38
+ <Breadcrumb.Item href="#" onClick={handleClick} active={false}>
39
+ Click Me
40
+ </Breadcrumb.Item>
41
+ <Breadcrumb.Item>Current</Breadcrumb.Item>
42
+ </Breadcrumb>
43
+ );
44
+
45
+ fireEvent.click(screen.getByText('Click Me'));
46
+ expect(handleClick).toHaveBeenCalledTimes(1);
47
+ });
48
+
49
+ it('supports custom LinkComponent in compound components', () => {
50
+ const CustomLink = ({ href, children, ...props }: any) => (
51
+ <a href={href} data-testid="custom-link" {...props}>
52
+ {children} (Custom)
53
+ </a>
54
+ );
55
+
56
+ render(
57
+ <Breadcrumb LinkComponent={CustomLink}>
58
+ <Breadcrumb.Item href="/custom">Link</Breadcrumb.Item>
59
+ <Breadcrumb.Item>Current</Breadcrumb.Item>
60
+ </Breadcrumb>
61
+ );
62
+
63
+ const link = screen.getByTestId('custom-link');
64
+ expect(link).toHaveAttribute('href', '/custom');
65
+ expect(link).toHaveTextContent('Link (Custom)');
66
+ });
67
+
68
+ it('supports explicit linkAs prop on Item', () => {
69
+ const CustomLink = ({ href, children, ...props }: any) => (
70
+ <a href={href} data-testid="item-custom-link" {...props}>
71
+ {children}
72
+ </a>
73
+ );
74
+
75
+ render(
76
+ <Breadcrumb>
77
+ <Breadcrumb.Item href="/explicit" linkAs={CustomLink} active={false}>Explicit</Breadcrumb.Item>
78
+ <Breadcrumb.Item>Current</Breadcrumb.Item>
79
+ </Breadcrumb>
80
+ );
81
+
82
+ expect(screen.getByTestId('item-custom-link')).toHaveAttribute('href', '/explicit');
83
+ });
84
+ });
@@ -1,3 +1,3 @@
1
- export { Breadcrumb } from './Breadcrumb';
2
1
  export { default } from './Breadcrumb';
3
- export type { BreadcrumbProps, BreadcrumbItem } from './Breadcrumb';
2
+ export type { BreadcrumbProps, BreadcrumbItemProps, BreadcrumbType } from './Breadcrumb';
3
+ export type { BreadcrumbItemLegacy as BreadcrumbItem } from './Breadcrumb';
@@ -774,7 +774,7 @@ export const WithCustomGlassSettings: Story = {
774
774
  blurAmount: 2,
775
775
  saturation: 200,
776
776
  aberrationIntensity: 1,
777
- cornerRadius: 16,
777
+ borderRadius: 16,
778
778
  mode: 'polar',
779
779
  },
780
780
  },