@donkit-ai/design-system 0.2.14 → 0.2.15

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.
package/README.md CHANGED
@@ -37,8 +37,8 @@ npm install @donkit-ai/design-system@latest
37
37
 
38
38
  ## Компоненты
39
39
 
40
- - **Button** - кнопки с вариантами (primary, secondary, ghost) и размерами (small, medium, large)
41
- - **Tabs** - вкладки с состоянием selected в трех размерах (small, medium, large)
40
+ - **Button** - кнопки с вариантами (primary, secondary, ghost) и размерами (small, medium, large). Поддерживает `href` для рендера как ссылка
41
+ - **Tabs** - вкладки с состоянием selected в трех размерах (small, medium, large). Поддерживает `href` для рендера как ссылка
42
42
  - **Input** - текстовые поля с поддержкой иконок, ошибок и подсказок (small, medium)
43
43
  - **Stepper** - числовое поле с кнопками +/- для изменения значения (small, medium)
44
44
  - **Card** - карточки двух типов: info (информационная, прозрачный фон) и interactive (интерактивная с hover эффектом)
@@ -81,6 +81,17 @@ function MyComponent() {
81
81
  </Card>
82
82
  );
83
83
  }
84
+
85
+ // Button как ссылка (рендерит <a> вместо <button>)
86
+ // При обычном клике: вызывается onClick (preventDefault автоматический)
87
+ // При Cmd/Ctrl+Click или Middle Click: открывается новая вкладка
88
+ <Button variant="primary" href="/dashboard" onClick={() => navigate('/dashboard')}>
89
+ Go to Dashboard
90
+ </Button>
91
+
92
+ <Button variant="secondary" href="https://example.com">
93
+ Visit Example
94
+ </Button>
84
95
  ```
85
96
 
86
97
  ### 3. Иконки
@@ -384,15 +395,16 @@ import { AlertCircle } from 'lucide-react';
384
395
  </Tabs>
385
396
 
386
397
  // As links (с href) - рендерит <a> вместо <button>
387
- // Поддерживает открытие в новой вкладке и работает с роутингом
398
+ // При обычном клике: вызывается onClick (preventDefault автоматический)
399
+ // При Cmd/Ctrl+Click или Middle Click: открывается новая вкладка
388
400
  <Tabs size="medium">
389
- <Tab selected href="/overview">
401
+ <Tab selected href="/overview" onClick={() => navigate('/overview')}>
390
402
  Overview
391
403
  </Tab>
392
- <Tab href="/details">
404
+ <Tab href="/details" onClick={() => navigate('/details')}>
393
405
  Details
394
406
  </Tab>
395
- <Tab href="/settings">
407
+ <Tab href="/settings" onClick={() => navigate('/settings')}>
396
408
  Settings
397
409
  </Tab>
398
410
  </Tabs>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@donkit-ai/design-system",
3
- "version": "0.2.14",
3
+ "version": "0.2.15",
4
4
  "description": "Donkit Design System - minimal design tokens and React components",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -13,11 +13,14 @@
13
13
  border-color var(--transition-normal),
14
14
  opacity var(--transition-normal);
15
15
  white-space: nowrap;
16
+ text-decoration: none;
16
17
  }
17
18
 
18
- .ds-button:disabled {
19
+ .ds-button:disabled,
20
+ .ds-button[aria-disabled="true"] {
19
21
  opacity: 0.5;
20
22
  cursor: not-allowed;
23
+ pointer-events: none;
21
24
  }
22
25
 
23
26
  .ds-button__icon {
@@ -32,7 +35,7 @@
32
35
  background-color: var(--color-accent);
33
36
  }
34
37
 
35
- .ds-button--primary:hover:not(:disabled) {
38
+ .ds-button--primary:hover:not(:disabled):not([aria-disabled="true"]) {
36
39
  background-color: var(--color-accent-hover);
37
40
  }
38
41
 
@@ -42,7 +45,7 @@
42
45
  border: 1px solid var(--color-border);
43
46
  }
44
47
 
45
- .ds-button--secondary:hover:not(:disabled) {
48
+ .ds-button--secondary:hover:not(:disabled):not([aria-disabled="true"]) {
46
49
  background-color: var(--color-item-bg-hover);
47
50
  border-color: var(--color-border-hover);
48
51
  }
@@ -52,7 +55,7 @@
52
55
  background-color: transparent;
53
56
  }
54
57
 
55
- .ds-button--ghost:hover:not(:disabled) {
58
+ .ds-button--ghost:hover:not(:disabled):not([aria-disabled="true"]) {
56
59
  background-color: var(--color-item-bg-hover);
57
60
  color: var(--color-txt-icon-1);
58
61
  }
@@ -10,6 +10,7 @@ export function Button({
10
10
  disabled = false,
11
11
  onClick,
12
12
  type = 'button',
13
+ href,
13
14
  'aria-label': ariaLabel,
14
15
  ...props
15
16
  }) {
@@ -23,6 +24,47 @@ export function Button({
23
24
  isIconOnly && 'ds-button--icon-only',
24
25
  ].filter(Boolean).join(' ');
25
26
 
27
+ const content = (
28
+ <>
29
+ {icon && !isIconOnly && <span className="ds-button__icon" aria-hidden="true">{icon}</span>}
30
+ {children}
31
+ {isIconOnly && <span className="ds-button__icon" aria-hidden="true">{icon}</span>}
32
+ </>
33
+ );
34
+
35
+ // Render as link if href is provided
36
+ if (href) {
37
+ const handleClick = (e) => {
38
+ if (disabled) {
39
+ e.preventDefault();
40
+ return;
41
+ }
42
+
43
+ // При Cmd/Ctrl+Click или Middle Click — пусть браузер откроет новую вкладку
44
+ if (e.metaKey || e.ctrlKey || e.button === 1) {
45
+ return;
46
+ }
47
+
48
+ // Обычный клик — preventDefault и вызов onClick
49
+ e.preventDefault();
50
+ onClick?.(e);
51
+ };
52
+
53
+ return (
54
+ <a
55
+ className={className}
56
+ href={disabled ? undefined : href}
57
+ onClick={handleClick}
58
+ aria-label={ariaLabel}
59
+ aria-disabled={disabled ? 'true' : undefined}
60
+ {...props}
61
+ >
62
+ {content}
63
+ </a>
64
+ );
65
+ }
66
+
67
+ // Render as button (default)
26
68
  return (
27
69
  <button
28
70
  type={type}
@@ -32,9 +74,7 @@ export function Button({
32
74
  aria-label={ariaLabel}
33
75
  {...props}
34
76
  >
35
- {icon && !isIconOnly && <span className="ds-button__icon" aria-hidden="true">{icon}</span>}
36
- {children}
37
- {isIconOnly && <span className="ds-button__icon" aria-hidden="true">{icon}</span>}
77
+ {content}
38
78
  </button>
39
79
  );
40
80
  }
@@ -34,6 +34,22 @@ export function Tab({ children, selected = false, onClick, size = 'medium', vari
34
34
 
35
35
  // Render as link if href is provided
36
36
  if (href) {
37
+ const handleClick = (e) => {
38
+ if (disabled) {
39
+ e.preventDefault();
40
+ return;
41
+ }
42
+
43
+ // При Cmd/Ctrl+Click или Middle Click — пусть браузер откроет новую вкладку
44
+ if (e.metaKey || e.ctrlKey || e.button === 1) {
45
+ return;
46
+ }
47
+
48
+ // Обычный клик — preventDefault и вызов onClick
49
+ e.preventDefault();
50
+ onClick?.(e);
51
+ };
52
+
37
53
  return (
38
54
  <a
39
55
  role="tab"
@@ -41,7 +57,7 @@ export function Tab({ children, selected = false, onClick, size = 'medium', vari
41
57
  aria-disabled={disabled ? 'true' : undefined}
42
58
  className={className}
43
59
  href={disabled ? undefined : href}
44
- onClick={disabled ? (e) => e.preventDefault() : onClick}
60
+ onClick={handleClick}
45
61
  {...props}
46
62
  >
47
63
  {content}