@shohojdhara/atomix 0.5.5 → 0.5.6

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 (37) hide show
  1. package/README.md +43 -21
  2. package/dist/atomix.css +647 -1395
  3. package/dist/atomix.css.map +1 -1
  4. package/dist/atomix.min.css +5 -5
  5. package/dist/atomix.min.css.map +1 -1
  6. package/dist/core.d.ts +100 -8
  7. package/dist/core.js +89 -79
  8. package/dist/core.js.map +1 -1
  9. package/dist/forms.js +1 -7
  10. package/dist/forms.js.map +1 -1
  11. package/dist/heavy.js +7 -3
  12. package/dist/heavy.js.map +1 -1
  13. package/dist/index.d.ts +179 -54
  14. package/dist/index.esm.js +112 -99
  15. package/dist/index.esm.js.map +1 -1
  16. package/dist/index.js +112 -99
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.min.js +1 -1
  19. package/dist/index.min.js.map +1 -1
  20. package/package.json +1 -1
  21. package/src/components/Accordion/Accordion.tsx +40 -25
  22. package/src/components/Breadcrumb/Breadcrumb.tsx +22 -13
  23. package/src/components/Button/Button.tsx +4 -5
  24. package/src/components/Callout/Callout.tsx +98 -96
  25. package/src/components/Card/Card.tsx +117 -103
  26. package/src/components/Card/index.ts +7 -5
  27. package/src/components/Dropdown/Dropdown.tsx +27 -8
  28. package/src/components/EdgePanel/EdgePanel.tsx +7 -2
  29. package/src/components/Modal/Modal.tsx +27 -8
  30. package/src/components/Spinner/Spinner.tsx +60 -43
  31. package/src/components/Tabs/Tabs.tsx +163 -149
  32. package/src/lib/composables/useInput.ts +11 -9
  33. package/src/lib/types/components.ts +84 -0
  34. package/src/styles/01-settings/_settings.background.scss +2 -1
  35. package/src/styles/02-tools/_tools.background.scss +100 -294
  36. package/src/styles/06-components/_components.card.scss +2 -2
  37. package/src/styles/99-utilities/_utilities.link.scss +4 -5
@@ -1,8 +1,26 @@
1
- import React, { useState, ReactNode, memo, createContext, useContext, forwardRef } from 'react';
1
+ import React, {
2
+ useState,
3
+ ReactNode,
4
+ memo,
5
+ createContext,
6
+ useContext,
7
+ forwardRef,
8
+ ComponentType,
9
+ } from 'react';
2
10
  import { TAB } from '../../lib/constants/components';
3
11
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
4
12
  import { AtomixGlassProps } from '../../lib/types/components';
5
13
 
14
+ // Type aliases for compound component detection
15
+ type ExtendedComponentType<P = {}> = ComponentType<P> & {
16
+ displayName?: string;
17
+ };
18
+
19
+ // Props interfaces for type-safe component props access
20
+ interface ExtendedReactElementProps {
21
+ children?: ReactNode;
22
+ }
23
+
6
24
  export interface TabsItemProps {
7
25
  /**
8
26
  * Label for the tab
@@ -87,7 +105,7 @@ export const TabsList = forwardRef<HTMLUListElement, React.HTMLAttributes<HTMLUL
87
105
  ref={ref}
88
106
  className={`c-tabs__nav ${className}`.trim()}
89
107
  role="tablist"
90
- onKeyDown={(e) => {
108
+ onKeyDown={e => {
91
109
  contextHandleKeyDown(e, totalTabs);
92
110
  onKeyDown?.(e);
93
111
  }}
@@ -95,8 +113,7 @@ export const TabsList = forwardRef<HTMLUListElement, React.HTMLAttributes<HTMLUL
95
113
  >
96
114
  {React.Children.map(children, (child, index) => {
97
115
  if (React.isValidElement(child)) {
98
- // Inject index into TabsTrigger
99
- return React.cloneElement(child, { index } as any);
116
+ return React.cloneElement(child as React.ReactElement<any>, { index });
100
117
  }
101
118
  return child;
102
119
  })}
@@ -127,7 +144,7 @@ export const TabsTrigger = forwardRef<HTMLButtonElement, TabsTriggerProps>(
127
144
  ref={ref}
128
145
  id={`tab-nav-${index}`}
129
146
  className={`c-tabs__nav-btn ${isActive ? TAB.CLASSES.ACTIVE : ''} ${className}`.trim()}
130
- onClick={(e) => {
147
+ onClick={e => {
131
148
  if (index !== undefined) handleTabClick(index);
132
149
  onClick?.(e);
133
150
  }}
@@ -152,10 +169,10 @@ export const TabsPanels = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDi
152
169
  return (
153
170
  <div ref={ref} className={`c-tabs__panels ${className}`.trim()} {...props}>
154
171
  {React.Children.map(children, (child, index) => {
155
- if (React.isValidElement(child)) {
156
- return React.cloneElement(child, { index } as any);
157
- }
158
- return child;
172
+ if (React.isValidElement(child)) {
173
+ return React.cloneElement(child as React.ReactElement<any>, { index });
174
+ }
175
+ return child;
159
176
  })}
160
177
  </div>
161
178
  );
@@ -196,7 +213,6 @@ export const TabsPanel = forwardRef<HTMLDivElement, TabsPanelProps>(
196
213
  );
197
214
  TabsPanel.displayName = 'TabsPanel';
198
215
 
199
-
200
216
  /**
201
217
  * Tabs component for switching between different content panels
202
218
  */
@@ -208,153 +224,151 @@ type TabsComponent = React.FC<TabsProps> & {
208
224
  };
209
225
 
210
226
  const TabsComponentBase = ({
211
- items,
212
- activeIndex = TAB.DEFAULTS.ACTIVE_INDEX,
213
- onTabChange,
214
- className = '',
215
- style,
216
- glass,
217
- children,
218
- }: TabsProps) => {
219
- const [currentTab, setCurrentTab] = useState(activeIndex);
220
-
221
- // Handle tab change
222
- const handleTabClick = (index: number) => {
223
- setCurrentTab(index);
224
- if (onTabChange) {
225
- onTabChange(index);
226
- }
227
- };
228
-
229
- // Keyboard navigation
230
- const handleKeyDown = (event: React.KeyboardEvent, totalTabs: number) => {
231
- let newIndex = currentTab;
232
- switch (event.key) {
233
- case 'ArrowRight':
234
- newIndex = (currentTab + 1) % totalTabs;
235
- break;
236
- case 'ArrowLeft':
237
- newIndex = (currentTab - 1 + totalTabs) % totalTabs;
238
- break;
239
- case 'Home':
240
- newIndex = 0;
241
- break;
242
- case 'End':
243
- newIndex = totalTabs - 1;
244
- break;
245
- default:
246
- return;
227
+ items,
228
+ activeIndex = TAB.DEFAULTS.ACTIVE_INDEX,
229
+ onTabChange,
230
+ className = '',
231
+ style,
232
+ glass,
233
+ children,
234
+ }: TabsProps) => {
235
+ const [currentTab, setCurrentTab] = useState(activeIndex);
236
+
237
+ // Handle tab change
238
+ const handleTabClick = (index: number) => {
239
+ setCurrentTab(index);
240
+ if (onTabChange) {
241
+ onTabChange(index);
242
+ }
243
+ };
244
+
245
+ // Keyboard navigation
246
+ const handleKeyDown = (event: React.KeyboardEvent, totalTabs: number) => {
247
+ let newIndex = currentTab;
248
+ switch (event.key) {
249
+ case 'ArrowRight':
250
+ newIndex = (currentTab + 1) % totalTabs;
251
+ break;
252
+ case 'ArrowLeft':
253
+ newIndex = (currentTab - 1 + totalTabs) % totalTabs;
254
+ break;
255
+ case 'Home':
256
+ newIndex = 0;
257
+ break;
258
+ case 'End':
259
+ newIndex = totalTabs - 1;
260
+ break;
261
+ default:
262
+ return;
263
+ }
264
+ event.preventDefault();
265
+ handleTabClick(newIndex);
266
+
267
+ // Focus the newly active tab after it renders
268
+ setTimeout(() => {
269
+ const tabElement = document.getElementById(`tab-nav-${newIndex}`);
270
+ if (tabElement) {
271
+ tabElement.focus();
247
272
  }
248
- event.preventDefault();
249
- handleTabClick(newIndex);
250
-
251
- // Focus the newly active tab after it renders
252
- setTimeout(() => {
253
- const tabElement = document.getElementById(`tab-nav-${newIndex}`);
254
- if (tabElement) {
255
- tabElement.focus();
256
- }
257
- }, 0);
258
- };
259
-
260
- // Determine content based on mode (legacy items vs compound children)
261
- let content: ReactNode;
262
-
263
- // Use items prop if provided
264
- if (items && items.length > 0) {
265
- // Legacy mode
266
- content = (
267
- <>
268
- <ul
269
- className="c-tabs__nav"
270
- role="tablist"
271
- onKeyDown={(e) => handleKeyDown(e, items.length)}
272
- >
273
- {items.map((item, index) => (
274
- <li className="c-tabs__nav-item" key={`tab-nav-${index}`} role="presentation">
275
- <button
276
- id={`tab-nav-${index}`}
277
- className={`c-tabs__nav-btn ${index === currentTab ? TAB.CLASSES.ACTIVE : ''}`}
278
- onClick={() => handleTabClick(index)}
279
- data-tabindex={index}
280
- role="tab"
281
- aria-selected={index === currentTab}
282
- aria-controls={`tab-panel-${index}`}
283
- tabIndex={index === currentTab ? 0 : -1}
284
- type="button"
285
- >
286
- {item.label}
287
- </button>
288
- </li>
289
- ))}
290
- </ul>
291
- <div className="c-tabs__panels">
292
- {items.map((item, index) => (
293
- <div
294
- className={`c-tabs__panel ${index === currentTab ? TAB.CLASSES.ACTIVE : ''}`}
295
- key={`tab-panel-${index}`}
273
+ }, 0);
274
+ };
275
+
276
+ // Determine content based on mode (legacy items vs compound children)
277
+ let content: ReactNode;
278
+
279
+ // Use items prop if provided
280
+ if (items && items.length > 0) {
281
+ // Legacy mode
282
+ content = (
283
+ <>
284
+ <ul className="c-tabs__nav" role="tablist" onKeyDown={e => handleKeyDown(e, items.length)}>
285
+ {items.map((item, index) => (
286
+ <li className="c-tabs__nav-item" key={`tab-nav-${index}`} role="presentation">
287
+ <button
288
+ id={`tab-nav-${index}`}
289
+ className={`c-tabs__nav-btn ${index === currentTab ? TAB.CLASSES.ACTIVE : ''}`}
290
+ onClick={() => handleTabClick(index)}
296
291
  data-tabindex={index}
297
- id={`tab-panel-${index}`}
298
- role="tabpanel"
299
- aria-labelledby={`tab-nav-${index}`}
300
- style={{
301
- height: index === currentTab ? 'auto' : '0px',
302
- opacity: index === currentTab ? 1 : 0,
303
- overflow: 'hidden',
304
- transition: 'height 0.3s ease, opacity 0.3s ease',
305
- }}
292
+ role="tab"
293
+ aria-selected={index === currentTab}
294
+ aria-controls={`tab-panel-${index}`}
295
+ tabIndex={index === currentTab ? 0 : -1}
296
+ type="button"
306
297
  >
307
- <div className="c-tabs__panel-body">{item.content}</div>
308
- </div>
309
- ))}
310
- </div>
311
- </>
312
- );
313
- } else {
314
- // Compound mode
315
- const tabsList = React.Children.toArray(children).find(
316
- (child): child is React.ReactElement =>
317
- React.isValidElement(child) && (child.type as any).displayName === 'TabsList'
318
- );
319
- const totalTabsCount = tabsList ? React.Children.count((tabsList.props as any).children) : 0;
320
-
321
- content = (
322
- <TabsContext.Provider
323
- value={{
324
- currentTab,
325
- handleTabClick,
326
- handleKeyDown,
327
- totalTabs: totalTabsCount,
328
- }}
329
- >
330
- {children}
331
- </TabsContext.Provider>
332
- );
333
- }
334
-
335
- const wrapper = (
336
- <div className={`c-tabs js-atomix-tab ${className}`} style={style}>
337
- {content}
338
- </div>
298
+ {item.label}
299
+ </button>
300
+ </li>
301
+ ))}
302
+ </ul>
303
+ <div className="c-tabs__panels">
304
+ {items.map((item, index) => (
305
+ <div
306
+ className={`c-tabs__panel ${index === currentTab ? TAB.CLASSES.ACTIVE : ''}`}
307
+ key={`tab-panel-${index}`}
308
+ data-tabindex={index}
309
+ id={`tab-panel-${index}`}
310
+ role="tabpanel"
311
+ aria-labelledby={`tab-nav-${index}`}
312
+ style={{
313
+ height: index === currentTab ? 'auto' : '0px',
314
+ opacity: index === currentTab ? 1 : 0,
315
+ overflow: 'hidden',
316
+ transition: 'height 0.3s ease, opacity 0.3s ease',
317
+ }}
318
+ >
319
+ <div className="c-tabs__panel-body">{item.content}</div>
320
+ </div>
321
+ ))}
322
+ </div>
323
+ </>
339
324
  );
325
+ } else {
326
+ // Compound mode
327
+ const tabsList = React.Children.toArray(children).find(
328
+ (child): child is React.ReactElement =>
329
+ React.isValidElement(child) &&
330
+ (child.type as ExtendedComponentType).displayName === 'TabsList'
331
+ ) as React.ReactElement | undefined;
332
+
333
+ const totalTabsCount = tabsList ? React.Children.count((tabsList as React.ReactElement<{ children: any }>).props.children) : 0;
334
+
335
+ content = (
336
+ <TabsContext.Provider
337
+ value={{
338
+ currentTab,
339
+ handleTabClick,
340
+ handleKeyDown,
341
+ totalTabs: totalTabsCount,
342
+ }}
343
+ >
344
+ {children}
345
+ </TabsContext.Provider>
346
+ );
347
+ }
340
348
 
341
- if (glass) {
342
- // Default glass settings for tabs
343
- const defaultGlassProps = {
344
- displacementScale: 60,
345
- blurAmount: 1,
346
- saturation: 160,
347
- aberrationIntensity: 0.5,
348
- borderRadius: 8,
349
- mode: 'shader' as const,
350
- };
349
+ const wrapper = (
350
+ <div className={`c-tabs js-atomix-tab ${className}`} style={style}>
351
+ {content}
352
+ </div>
353
+ );
354
+
355
+ if (glass) {
356
+ // Default glass settings for tabs
357
+ const defaultGlassProps = {
358
+ displacementScale: 60,
359
+ blurAmount: 1,
360
+ saturation: 160,
361
+ aberrationIntensity: 0.5,
362
+ borderRadius: 8,
363
+ mode: 'shader' as const,
364
+ };
351
365
 
352
- const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
366
+ const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
353
367
 
354
- return <AtomixGlass {...glassProps}>{wrapper}</AtomixGlass>;
355
- }
368
+ return <AtomixGlass {...glassProps}>{wrapper}</AtomixGlass>;
369
+ }
356
370
 
357
- return wrapper;
371
+ return wrapper;
358
372
  };
359
373
 
360
374
  export const Tabs = memo(TabsComponentBase) as unknown as TabsComponent;
@@ -6,15 +6,17 @@ import { INPUT } from '../constants/components';
6
6
  * @param initialProps - Initial input properties
7
7
  * @returns Input state and methods
8
8
  */
9
+ interface UseInputOptions {
10
+ prefixIcon?: boolean;
11
+ suffixIcon?: boolean;
12
+ clearable?: boolean;
13
+ showCounter?: boolean;
14
+ showPasswordToggle?: boolean;
15
+ fullWidth?: boolean;
16
+ }
17
+
9
18
  export function useInput(
10
- initialProps?: Partial<InputProps> & {
11
- prefixIcon?: boolean;
12
- suffixIcon?: boolean;
13
- clearable?: boolean;
14
- showCounter?: boolean;
15
- showPasswordToggle?: boolean;
16
- fullWidth?: boolean;
17
- }
19
+ initialProps?: Partial<InputProps> & UseInputOptions
18
20
  ) {
19
21
  // Default input properties
20
22
  const defaultProps: Partial<InputProps> = {
@@ -74,7 +76,7 @@ export function useInput(
74
76
  showCounter = false,
75
77
  showPasswordToggle = false,
76
78
  fullWidth = false,
77
- } = initialProps || {};
79
+ } = (initialProps as UseInputOptions) || {};
78
80
 
79
81
  const classes = [INPUT.ELEMENTS.WRAPPER];
80
82
 
@@ -2183,6 +2183,90 @@ export interface TodoProps extends BaseComponentProps {
2183
2183
  glass?: AtomixGlassProps | boolean;
2184
2184
  }
2185
2185
 
2186
+ /**
2187
+ * Spinner component properties
2188
+ */
2189
+ export interface SpinnerProps extends BaseComponentProps {
2190
+ /**
2191
+ * Spinner color variant
2192
+ * @default 'primary'
2193
+ */
2194
+ variant?: ThemeColor;
2195
+
2196
+ /**
2197
+ * Spinner size
2198
+ * @default 'md'
2199
+ */
2200
+ size?: Size;
2201
+
2202
+ /**
2203
+ * Whether the spinner should be displayed fullscreen
2204
+ */
2205
+ fullscreen?: boolean;
2206
+
2207
+ /**
2208
+ * Accessible label for screen readers
2209
+ * @default 'Loading'
2210
+ */
2211
+ 'aria-label'?: string;
2212
+
2213
+ /**
2214
+ * ARIA live property to control how updates are announced
2215
+ * @default 'polite'
2216
+ */
2217
+ 'aria-live'?: 'off' | 'polite' | 'assertive';
2218
+
2219
+ /**
2220
+ * ARIA descriptor property for additional description
2221
+ */
2222
+ 'aria-describe'?: string;
2223
+
2224
+ /**
2225
+ * ARIA role for the spinner
2226
+ * @default 'status'
2227
+ */
2228
+ role?: 'status' | 'alert';
2229
+
2230
+ /**
2231
+ * Glass morphism effect for the spinner
2232
+ * Can be a boolean to enable with default settings, or an object with AtomixGlassProps to customize the effect
2233
+ */
2234
+ glass?: AtomixGlassProps | boolean;
2235
+ }
2236
+
2237
+ /**
2238
+ * Icon size options
2239
+ */
2240
+ export type IconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
2241
+
2242
+ /**
2243
+ * Icon component properties
2244
+ */
2245
+ export interface IconProps extends BaseComponentProps {
2246
+ /**
2247
+ * Icon name
2248
+ */
2249
+ name: string;
2250
+
2251
+ /**
2252
+ * Icon size
2253
+ * @default 'md'
2254
+ */
2255
+ size?: IconSize;
2256
+
2257
+ /**
2258
+ * Icon color variant
2259
+ * @default 'inherit'
2260
+ */
2261
+ variant?: ThemeColor;
2262
+
2263
+ /**
2264
+ * Glass morphism effect for the icon
2265
+ * Can be a boolean to enable with default settings, or an object with AtomixGlassProps to customize the effect
2266
+ */
2267
+ glass?: AtomixGlassProps | boolean;
2268
+ }
2269
+
2186
2270
  /**
2187
2271
  * Form component properties
2188
2272
  */
@@ -5,6 +5,7 @@ $background-enable-with-shadow: true !default;
5
5
  $background-enable-mesh-gradient: false !default;
6
6
  $background-enable-noise-texture: false !default;
7
7
  $background-enable-shimmer: false !default;
8
+ $background-enable-hover-effects: true !default; // Variable to control hover effects
8
9
 
9
10
  // Core transparency levels
10
11
  $background-transparency: 0.65 !default;
@@ -33,4 +34,4 @@ $background-noise-scale: 200 !default;
33
34
 
34
35
  // Shimmer effect
35
36
  $background-shimmer-duration: 3s !default;
36
- $background-shimmer-intensity: 0.15 !default;
37
+ $background-shimmer-intensity: 0.15 !default;