@attrus-ds/core 0.2.0 → 0.3.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.
Files changed (80) hide show
  1. package/LICENSE +27 -27
  2. package/dist/assets/flags/Brazil-rounded.svg +15 -15
  3. package/dist/assets/flags/Brazil-square.svg +16 -16
  4. package/dist/assets/flags/Chile-rounded.svg +10 -10
  5. package/dist/assets/flags/Chile-square.svg +10 -10
  6. package/dist/assets/flags/Colombia-rounded.svg +9 -9
  7. package/dist/assets/flags/Colombia-square.svg +9 -9
  8. package/dist/assets/flags/EUA-rounded.svg +10 -10
  9. package/dist/assets/flags/EUA-square.svg +10 -10
  10. package/dist/assets/flags/EUR-rounded.svg +8 -8
  11. package/dist/assets/flags/EUR-square.svg +8 -8
  12. package/dist/assets/flags/Mexico-rounded.svg +18 -18
  13. package/dist/assets/flags/Mexico-square.svg +18 -18
  14. package/dist/assets/icon-attrus-dark.svg +2 -2
  15. package/dist/assets/icon-attrus-light.svg +2 -2
  16. package/dist/assets/logo-attrus-dark.svg +7 -7
  17. package/dist/assets/logo-attrus-light.svg +7 -7
  18. package/dist/css/components/buttons.css +301 -301
  19. package/dist/css/components/callout.css +195 -195
  20. package/dist/css/components/cards.css +272 -272
  21. package/dist/css/components/combobox.css +319 -319
  22. package/dist/css/components/empty-states.css +61 -61
  23. package/dist/css/components/inputs.css +975 -975
  24. package/dist/css/components/number-input.css +138 -138
  25. package/dist/css/components/overlays.css +374 -374
  26. package/dist/css/components/primitives.css +314 -314
  27. package/dist/css/components/sidebar.css +179 -179
  28. package/dist/css/components/smart-search.css +91 -91
  29. package/dist/css/components/status-pills.css +207 -207
  30. package/dist/css/components/stepper.css +309 -309
  31. package/dist/css/components/table.css +602 -602
  32. package/dist/css/components/tabs.css +319 -319
  33. package/dist/css/components/toast.css +243 -243
  34. package/dist/css/components/tooltip.css +231 -231
  35. package/dist/css/components/topbar.css +148 -148
  36. package/dist/css/tokens.css +666 -666
  37. package/dist/react/AppSidebar/AppSidebar.d.ts +50 -50
  38. package/dist/react/AppSidebar/AppSidebar.tsx +197 -197
  39. package/dist/react/AppTopbar/AppTopbar.d.ts +58 -58
  40. package/dist/react/AppTopbar/AppTopbar.tsx +203 -203
  41. package/dist/react/Atoms/Atoms.d.ts +149 -149
  42. package/dist/react/Atoms/Atoms.tsx +407 -407
  43. package/dist/react/Button/Button.d.ts +25 -25
  44. package/dist/react/Button/Button.tsx +83 -83
  45. package/dist/react/Callout/Callout.d.ts +29 -29
  46. package/dist/react/Callout/Callout.tsx +98 -98
  47. package/dist/react/Card/Card.d.ts +38 -38
  48. package/dist/react/Card/Card.tsx +93 -93
  49. package/dist/react/Combobox/Combobox.d.ts +52 -52
  50. package/dist/react/Combobox/Combobox.tsx +254 -254
  51. package/dist/react/DataTable/DataTable.d.ts +81 -81
  52. package/dist/react/DataTable/DataTable.tsx +308 -308
  53. package/dist/react/Drawer/Drawer.d.ts +35 -35
  54. package/dist/react/Drawer/Drawer.tsx +226 -226
  55. package/dist/react/Input/Input.d.ts +155 -155
  56. package/dist/react/Input/Input.tsx +478 -478
  57. package/dist/react/Modal/Modal.d.ts +27 -27
  58. package/dist/react/Modal/Modal.tsx +173 -173
  59. package/dist/react/NumberInput/NumberInput.d.ts +25 -25
  60. package/dist/react/NumberInput/NumberInput.tsx +117 -117
  61. package/dist/react/Pill/Pill.d.ts +31 -31
  62. package/dist/react/Pill/Pill.tsx +104 -104
  63. package/dist/react/SearchPill/SearchPill.d.ts +14 -14
  64. package/dist/react/SearchPill/SearchPill.tsx +40 -40
  65. package/dist/react/SmartSearch/SmartSearch.d.ts +65 -65
  66. package/dist/react/SmartSearch/SmartSearch.tsx +271 -271
  67. package/dist/react/Stepper/Stepper.d.ts +25 -25
  68. package/dist/react/Stepper/Stepper.tsx +94 -94
  69. package/dist/react/Tabs/Tabs.d.ts +32 -32
  70. package/dist/react/Tabs/Tabs.tsx +147 -147
  71. package/dist/react/Toast/Toast.d.ts +30 -30
  72. package/dist/react/Toast/Toast.tsx +94 -94
  73. package/dist/react/Tooltip/Tooltip.d.ts +23 -23
  74. package/dist/react/Tooltip/Tooltip.tsx +80 -80
  75. package/dist/react/ZeroState/ZeroState.d.ts +17 -17
  76. package/dist/react/ZeroState/ZeroState.tsx +45 -45
  77. package/dist/react/index.ts +35 -35
  78. package/dist/tokens/attrus-tokens.flat.json +1234 -1234
  79. package/dist/tokens/attrus-tokens.json +1089 -1089
  80. package/package.json +64 -64
@@ -1,203 +1,203 @@
1
- import * as React from 'react';
2
-
3
- /**
4
- * ATTRUS AppTopbar — typed wrapper over the canonical `.topbar` system
5
- * (preview/components/topbar.css). The app's top edge: primary action,
6
- * global search, and a right-aligned cluster of 36px controls
7
- * (--tb-control-h drives every item). Includes the live indicator,
8
- * notification bell and the account menu (identity + dropdown).
9
- * (Named AppTopbar — "Shell"/"TopBar" collide with legacy app files.)
10
- */
11
-
12
- export interface AppTopbarProps extends React.HTMLAttributes<HTMLDivElement> {
13
- /** Left CTA slot — the canonical Button (primary). */
14
- primary?: React.ReactNode;
15
- /** Search slot — the canonical SearchPill (grows to fill). */
16
- search?: React.ReactNode;
17
- /** Right-aligned cluster (.tb-spacer): TopbarControl/Live/Bell/Account. */
18
- children?: React.ReactNode;
19
- }
20
-
21
- export const AppTopbar: React.FC<AppTopbarProps> = ({ primary, search, children, className, ...rest }) => (
22
- <div className={['topbar', className || ''].filter(Boolean).join(' ')} {...rest}>
23
- {primary}
24
- {search}
25
- <div className="tb-spacer">{children}</div>
26
- </div>
27
- );
28
-
29
- /* ---------- Generic 36px control ---------- */
30
-
31
- export interface TopbarControlProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
32
- /** Leading slot (icon, flag…). */
33
- icon?: React.ReactNode;
34
- /** Square icon-only control (.tb-control.icon). */
35
- iconOnly?: boolean;
36
- /** Trailing disclosure chevron. */
37
- chev?: boolean;
38
- }
39
-
40
- const ChevDown: React.FC = () => (
41
- <span className="chev" aria-hidden="true">
42
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
43
- <path d="m6 9 6 6 6-6" />
44
- </svg>
45
- </span>
46
- );
47
-
48
- export const TopbarControl: React.FC<TopbarControlProps> = ({ icon, iconOnly = false, chev = false, className, children, ...rest }) => (
49
- <button
50
- type="button"
51
- className={['tb-control', iconOnly ? 'icon' : '', className || ''].filter(Boolean).join(' ')}
52
- {...rest}
53
- >
54
- {icon}
55
- {children}
56
- {chev ? <ChevDown /> : null}
57
- </button>
58
- );
59
-
60
- /* ---------- Live session indicator (non-interactive) ---------- */
61
-
62
- export interface TopbarLiveProps {
63
- label?: React.ReactNode;
64
- /** Elapsed mono clock (e.g. "0:19"). */
65
- clock?: React.ReactNode;
66
- }
67
-
68
- export const TopbarLive: React.FC<TopbarLiveProps> = ({ label = 'Live', clock }) => (
69
- <div className="tb-control tb-live">
70
- <span className="dot" />
71
- <span className="lbl">{label}</span>
72
- {clock != null ? <span className="tb-clock">{clock}</span> : null}
73
- </div>
74
- );
75
-
76
- /* ---------- Notification bell ---------- */
77
-
78
- export interface TopbarBellProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
79
- /** Unread red dot. */
80
- badge?: boolean;
81
- }
82
-
83
- export const TopbarBell: React.FC<TopbarBellProps> = ({ badge = false, className, ...rest }) => (
84
- <button
85
- type="button"
86
- className={['tb-control', 'icon', 'tb-bell', className || ''].filter(Boolean).join(' ')}
87
- aria-label="Notifications"
88
- {...rest}
89
- >
90
- <span className="badge-anchor">
91
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" style={{ width: 14, height: 14 }}>
92
- <path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" />
93
- <path d="M10.3 21a1.94 1.94 0 0 0 3.4 0" />
94
- </svg>
95
- {badge ? <span className="badge" /> : null}
96
- </span>
97
- </button>
98
- );
99
-
100
- /* ---------- Account menu (identity + dropdown) ---------- */
101
-
102
- export interface TopbarAccountItem {
103
- key: string;
104
- label: React.ReactNode;
105
- icon?: React.ReactNode;
106
- danger?: boolean;
107
- /** Renders a separator before this item. */
108
- sep?: boolean;
109
- }
110
-
111
- export interface TopbarAccountProps {
112
- initials: string;
113
- name: React.ReactNode;
114
- company: React.ReactNode;
115
- /** Second line in the dropdown head (e.g. "Acme Corp · Admin"). */
116
- detail?: React.ReactNode;
117
- items?: TopbarAccountItem[];
118
- onSelect?: (key: string) => void;
119
- /** Hide name/company in the trigger (mobile). */
120
- compact?: boolean;
121
- }
122
-
123
- export const TopbarAccount: React.FC<TopbarAccountProps> = ({
124
- initials,
125
- name,
126
- company,
127
- detail,
128
- items = [],
129
- onSelect,
130
- compact = false,
131
- }) => {
132
- const [open, setOpen] = React.useState(false);
133
- const wrapRef = React.useRef<HTMLDivElement>(null);
134
-
135
- React.useEffect(() => {
136
- if (!open) return undefined;
137
- const onDoc = (e: MouseEvent) => {
138
- if (wrapRef.current && !wrapRef.current.contains(e.target as Node)) setOpen(false);
139
- };
140
- const onKey = (e: KeyboardEvent) => {
141
- if (e.key === 'Escape') setOpen(false);
142
- };
143
- document.addEventListener('mousedown', onDoc);
144
- document.addEventListener('keydown', onKey);
145
- return () => {
146
- document.removeEventListener('mousedown', onDoc);
147
- document.removeEventListener('keydown', onKey);
148
- };
149
- }, [open]);
150
-
151
- return (
152
- <div className="tb-account-wrap" ref={wrapRef}>
153
- <button
154
- type="button"
155
- className={['tb-account', open ? 'is-open' : ''].filter(Boolean).join(' ')}
156
- aria-haspopup="menu"
157
- aria-expanded={open}
158
- aria-label="Account menu"
159
- onClick={() => setOpen((o) => !o)}
160
- >
161
- <span className="tb-avatar">{initials}</span>
162
- {!compact ? (
163
- <span className="tb-account-id">
164
- <span className="nm">{name}</span>
165
- <span className="co">{company}</span>
166
- </span>
167
- ) : null}
168
- {!compact ? <ChevDown /> : null}
169
- </button>
170
- {open ? (
171
- <div className="tb-account-menu" role="menu">
172
- <div className="tb-account-menu-head">
173
- <span className="tb-avatar lg">{initials}</span>
174
- <div className="tb-account-menu-id">
175
- <span className="nm">{name}</span>
176
- <span className="co">{detail != null ? detail : company}</span>
177
- </div>
178
- </div>
179
- <div className="tb-account-menu-sep" />
180
- {items.map((it) => (
181
- <React.Fragment key={it.key}>
182
- {it.sep ? <div className="tb-account-menu-sep" /> : null}
183
- <button
184
- type="button"
185
- className={['tb-account-menu-item', it.danger ? 'is-danger' : ''].filter(Boolean).join(' ')}
186
- role="menuitem"
187
- onClick={() => {
188
- setOpen(false);
189
- if (onSelect) onSelect(it.key);
190
- }}
191
- >
192
- {it.icon}
193
- {it.label}
194
- </button>
195
- </React.Fragment>
196
- ))}
197
- </div>
198
- ) : null}
199
- </div>
200
- );
201
- };
202
-
203
- export default AppTopbar;
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * ATTRUS AppTopbar — typed wrapper over the canonical `.topbar` system
5
+ * (preview/components/topbar.css). The app's top edge: primary action,
6
+ * global search, and a right-aligned cluster of 36px controls
7
+ * (--tb-control-h drives every item). Includes the live indicator,
8
+ * notification bell and the account menu (identity + dropdown).
9
+ * (Named AppTopbar — "Shell"/"TopBar" collide with legacy app files.)
10
+ */
11
+
12
+ export interface AppTopbarProps extends React.HTMLAttributes<HTMLDivElement> {
13
+ /** Left CTA slot — the canonical Button (primary). */
14
+ primary?: React.ReactNode;
15
+ /** Search slot — the canonical SearchPill (grows to fill). */
16
+ search?: React.ReactNode;
17
+ /** Right-aligned cluster (.tb-spacer): TopbarControl/Live/Bell/Account. */
18
+ children?: React.ReactNode;
19
+ }
20
+
21
+ export const AppTopbar: React.FC<AppTopbarProps> = ({ primary, search, children, className, ...rest }) => (
22
+ <div className={['topbar', className || ''].filter(Boolean).join(' ')} {...rest}>
23
+ {primary}
24
+ {search}
25
+ <div className="tb-spacer">{children}</div>
26
+ </div>
27
+ );
28
+
29
+ /* ---------- Generic 36px control ---------- */
30
+
31
+ export interface TopbarControlProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
32
+ /** Leading slot (icon, flag…). */
33
+ icon?: React.ReactNode;
34
+ /** Square icon-only control (.tb-control.icon). */
35
+ iconOnly?: boolean;
36
+ /** Trailing disclosure chevron. */
37
+ chev?: boolean;
38
+ }
39
+
40
+ const ChevDown: React.FC = () => (
41
+ <span className="chev" aria-hidden="true">
42
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
43
+ <path d="m6 9 6 6 6-6" />
44
+ </svg>
45
+ </span>
46
+ );
47
+
48
+ export const TopbarControl: React.FC<TopbarControlProps> = ({ icon, iconOnly = false, chev = false, className, children, ...rest }) => (
49
+ <button
50
+ type="button"
51
+ className={['tb-control', iconOnly ? 'icon' : '', className || ''].filter(Boolean).join(' ')}
52
+ {...rest}
53
+ >
54
+ {icon}
55
+ {children}
56
+ {chev ? <ChevDown /> : null}
57
+ </button>
58
+ );
59
+
60
+ /* ---------- Live session indicator (non-interactive) ---------- */
61
+
62
+ export interface TopbarLiveProps {
63
+ label?: React.ReactNode;
64
+ /** Elapsed mono clock (e.g. "0:19"). */
65
+ clock?: React.ReactNode;
66
+ }
67
+
68
+ export const TopbarLive: React.FC<TopbarLiveProps> = ({ label = 'Live', clock }) => (
69
+ <div className="tb-control tb-live">
70
+ <span className="dot" />
71
+ <span className="lbl">{label}</span>
72
+ {clock != null ? <span className="tb-clock">{clock}</span> : null}
73
+ </div>
74
+ );
75
+
76
+ /* ---------- Notification bell ---------- */
77
+
78
+ export interface TopbarBellProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
79
+ /** Unread red dot. */
80
+ badge?: boolean;
81
+ }
82
+
83
+ export const TopbarBell: React.FC<TopbarBellProps> = ({ badge = false, className, ...rest }) => (
84
+ <button
85
+ type="button"
86
+ className={['tb-control', 'icon', 'tb-bell', className || ''].filter(Boolean).join(' ')}
87
+ aria-label="Notifications"
88
+ {...rest}
89
+ >
90
+ <span className="badge-anchor">
91
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" style={{ width: 14, height: 14 }}>
92
+ <path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" />
93
+ <path d="M10.3 21a1.94 1.94 0 0 0 3.4 0" />
94
+ </svg>
95
+ {badge ? <span className="badge" /> : null}
96
+ </span>
97
+ </button>
98
+ );
99
+
100
+ /* ---------- Account menu (identity + dropdown) ---------- */
101
+
102
+ export interface TopbarAccountItem {
103
+ key: string;
104
+ label: React.ReactNode;
105
+ icon?: React.ReactNode;
106
+ danger?: boolean;
107
+ /** Renders a separator before this item. */
108
+ sep?: boolean;
109
+ }
110
+
111
+ export interface TopbarAccountProps {
112
+ initials: string;
113
+ name: React.ReactNode;
114
+ company: React.ReactNode;
115
+ /** Second line in the dropdown head (e.g. "Acme Corp · Admin"). */
116
+ detail?: React.ReactNode;
117
+ items?: TopbarAccountItem[];
118
+ onSelect?: (key: string) => void;
119
+ /** Hide name/company in the trigger (mobile). */
120
+ compact?: boolean;
121
+ }
122
+
123
+ export const TopbarAccount: React.FC<TopbarAccountProps> = ({
124
+ initials,
125
+ name,
126
+ company,
127
+ detail,
128
+ items = [],
129
+ onSelect,
130
+ compact = false,
131
+ }) => {
132
+ const [open, setOpen] = React.useState(false);
133
+ const wrapRef = React.useRef<HTMLDivElement>(null);
134
+
135
+ React.useEffect(() => {
136
+ if (!open) return undefined;
137
+ const onDoc = (e: MouseEvent) => {
138
+ if (wrapRef.current && !wrapRef.current.contains(e.target as Node)) setOpen(false);
139
+ };
140
+ const onKey = (e: KeyboardEvent) => {
141
+ if (e.key === 'Escape') setOpen(false);
142
+ };
143
+ document.addEventListener('mousedown', onDoc);
144
+ document.addEventListener('keydown', onKey);
145
+ return () => {
146
+ document.removeEventListener('mousedown', onDoc);
147
+ document.removeEventListener('keydown', onKey);
148
+ };
149
+ }, [open]);
150
+
151
+ return (
152
+ <div className="tb-account-wrap" ref={wrapRef}>
153
+ <button
154
+ type="button"
155
+ className={['tb-account', open ? 'is-open' : ''].filter(Boolean).join(' ')}
156
+ aria-haspopup="menu"
157
+ aria-expanded={open}
158
+ aria-label="Account menu"
159
+ onClick={() => setOpen((o) => !o)}
160
+ >
161
+ <span className="tb-avatar">{initials}</span>
162
+ {!compact ? (
163
+ <span className="tb-account-id">
164
+ <span className="nm">{name}</span>
165
+ <span className="co">{company}</span>
166
+ </span>
167
+ ) : null}
168
+ {!compact ? <ChevDown /> : null}
169
+ </button>
170
+ {open ? (
171
+ <div className="tb-account-menu" role="menu">
172
+ <div className="tb-account-menu-head">
173
+ <span className="tb-avatar lg">{initials}</span>
174
+ <div className="tb-account-menu-id">
175
+ <span className="nm">{name}</span>
176
+ <span className="co">{detail != null ? detail : company}</span>
177
+ </div>
178
+ </div>
179
+ <div className="tb-account-menu-sep" />
180
+ {items.map((it) => (
181
+ <React.Fragment key={it.key}>
182
+ {it.sep ? <div className="tb-account-menu-sep" /> : null}
183
+ <button
184
+ type="button"
185
+ className={['tb-account-menu-item', it.danger ? 'is-danger' : ''].filter(Boolean).join(' ')}
186
+ role="menuitem"
187
+ onClick={() => {
188
+ setOpen(false);
189
+ if (onSelect) onSelect(it.key);
190
+ }}
191
+ >
192
+ {it.icon}
193
+ {it.label}
194
+ </button>
195
+ </React.Fragment>
196
+ ))}
197
+ </div>
198
+ ) : null}
199
+ </div>
200
+ );
201
+ };
202
+
203
+ export default AppTopbar;