@attrus-ds/core 1.0.10 → 1.0.12

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 (50) hide show
  1. package/dist/css/components/action-bar.css +93 -0
  2. package/dist/css/components/ambient.css +97 -0
  3. package/dist/css/components/animated-bg.css +261 -0
  4. package/dist/css/components/buttons.css +4 -4
  5. package/dist/css/components/callout.css +4 -3
  6. package/dist/css/components/cards.css +4 -3
  7. package/dist/css/components/combobox.css +16 -13
  8. package/dist/css/components/inputs.css +99 -15
  9. package/dist/css/components/number-input.css +6 -3
  10. package/dist/css/components/primitives.css +20 -15
  11. package/dist/css/components/sel-card.css +212 -0
  12. package/dist/css/components/sidebar.css +11 -9
  13. package/dist/css/components/smart-search.css +13 -11
  14. package/dist/css/components/status-pills.css +1 -1
  15. package/dist/css/components/stepper.css +81 -4
  16. package/dist/css/components/table.css +168 -7
  17. package/dist/css/components/tabs.css +4 -3
  18. package/dist/css/components/toast.css +2 -2
  19. package/dist/css/components/toggle-chip.css +137 -0
  20. package/dist/css/components/tooltip.css +8 -5
  21. package/dist/css/components/topbar.css +8 -7
  22. package/dist/css/index.css +5 -0
  23. package/dist/css/tokens.css +126 -22
  24. package/dist/react/ActionBar/ActionBar.d.ts +19 -0
  25. package/dist/react/ActionBar/ActionBar.tsx +54 -0
  26. package/dist/react/AmbientBackdrop/AmbientBackdrop.d.ts +17 -0
  27. package/dist/react/AmbientBackdrop/AmbientBackdrop.tsx +53 -0
  28. package/dist/react/AnimatedBackdrop/AnimatedBackdrop.d.ts +22 -0
  29. package/dist/react/AnimatedBackdrop/AnimatedBackdrop.tsx +317 -0
  30. package/dist/react/Callout/Callout.d.ts +1 -1
  31. package/dist/react/Callout/Callout.tsx +1 -1
  32. package/dist/react/Combobox/Combobox.d.ts +2 -0
  33. package/dist/react/Combobox/Combobox.tsx +85 -39
  34. package/dist/react/DataTable/DataTable.tsx +3 -3
  35. package/dist/react/Drawer/Drawer.tsx +7 -1
  36. package/dist/react/Input/Input.d.ts +1 -1
  37. package/dist/react/Input/Input.tsx +1 -1
  38. package/dist/react/Modal/Modal.tsx +8 -1
  39. package/dist/react/SelectableCard/SelectableCard.d.ts +39 -0
  40. package/dist/react/SelectableCard/SelectableCard.tsx +94 -0
  41. package/dist/react/SmartSearch/SmartSearch.tsx +2 -2
  42. package/dist/react/Stepper/Stepper.d.ts +3 -0
  43. package/dist/react/Stepper/Stepper.tsx +7 -0
  44. package/dist/react/Toast/Toast.d.ts +1 -1
  45. package/dist/react/Toast/Toast.tsx +1 -1
  46. package/dist/react/ToggleChip/ToggleChip.d.ts +29 -0
  47. package/dist/react/ToggleChip/ToggleChip.tsx +76 -0
  48. package/dist/react/Tooltip/Tooltip.d.ts +1 -1
  49. package/dist/react/Tooltip/Tooltip.tsx +1 -1
  50. package/package.json +1 -1
@@ -367,7 +367,7 @@ export const Otp: React.FC<OtpProps> = ({ length = 6, value, onChange, error = f
367
367
  );
368
368
  };
369
369
 
370
- export interface FileDropProps extends React.HTMLAttributes<HTMLDivElement> {
370
+ export interface FileDropProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
371
371
  /** Called with the dropped/browsed files. */
372
372
  onFiles?: (files: File[]) => void;
373
373
  multiple?: boolean;
@@ -1,4 +1,5 @@
1
1
  import * as React from 'react';
2
+ import { createPortal } from 'react-dom';
2
3
 
3
4
  /**
4
5
  * ATTRUS Modal — typed wrapper over the canonical `.modal` + `.scrim`
@@ -140,7 +141,7 @@ export const Modal: React.FC<ModalProps> = ({
140
141
  .filter(Boolean)
141
142
  .join(' ');
142
143
 
143
- return (
144
+ const overlay = (
144
145
  <React.Fragment>
145
146
  {isStatic ? null : <div className={['scrim', shown ? 'is-open' : ''].filter(Boolean).join(' ')} onClick={onClose} />}
146
147
  <div ref={panelRef} className={cls} role="dialog" aria-modal="true" tabIndex={-1} onKeyDown={onTrapKeyDown}>
@@ -168,6 +169,12 @@ export const Modal: React.FC<ModalProps> = ({
168
169
  </div>
169
170
  </React.Fragment>
170
171
  );
172
+
173
+ // Portal the live overlay to <body> so the scrim + dialog escape any clipping
174
+ // / stacking ancestor (e.g. a Drawer: position:fixed; overflow:auto). Static
175
+ // (gallery/doc) renders inline inside its relative frame, as documented.
176
+ if (isStatic || typeof document === 'undefined') return overlay;
177
+ return createPortal(overlay, document.body);
171
178
  };
172
179
 
173
180
  export default Modal;
@@ -0,0 +1,39 @@
1
+ import * as React from 'react';
2
+
3
+ export interface SelectableCardProps {
4
+ /** Primary line. */
5
+ title: React.ReactNode;
6
+ /** Supporting copy under the title. */
7
+ desc?: React.ReactNode;
8
+ /** Leading index chip — "01", "02"… */
9
+ index?: React.ReactNode;
10
+ /** Selected state (controlled). */
11
+ selected?: boolean;
12
+ onSelect?: (next: boolean) => void;
13
+ /** Radio group name (single-select). */
14
+ name?: string;
15
+ value?: string;
16
+ /** Pick-many: square marker + checkbox input. */
17
+ multi?: boolean;
18
+ disabled?: boolean;
19
+ /** Dense padding + smaller type. */
20
+ size?: 'md' | 'sm';
21
+ /** Dark-surface treatment. */
22
+ onInverse?: boolean;
23
+ className?: string;
24
+ style?: React.CSSProperties;
25
+ }
26
+
27
+ export declare const SelectableCard: React.FC<SelectableCardProps>;
28
+
29
+ export interface SelectableCardGroupProps {
30
+ children?: React.ReactNode;
31
+ /** Auto-fit grid instead of a single column. */
32
+ grid?: boolean;
33
+ className?: string;
34
+ style?: React.CSSProperties;
35
+ }
36
+
37
+ export declare const SelectableCardGroup: React.FC<SelectableCardGroupProps>;
38
+
39
+ export default SelectableCard;
@@ -0,0 +1,94 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * ATTRUS SelectableCard — typed wrapper over the canonical `.sel-card`
5
+ * (preview/components/sel-card.css): a card that IS a control.
6
+ *
7
+ * Prefer this over hand-rolling `.card + .radio` in onboarding / KYC /
8
+ * plan-picker flows — that composition reinvents padding, radius and the
9
+ * selected state every time.
10
+ *
11
+ * `multi` switches the marker to a square (checkbox semantics) and renders a
12
+ * checkbox input; the default is radio semantics. Pass `name` to group radios.
13
+ */
14
+ export interface SelectableCardProps {
15
+ title: React.ReactNode;
16
+ /** Supporting copy under the title. */
17
+ desc?: React.ReactNode;
18
+ /** Leading index chip — "01", "02"… */
19
+ index?: React.ReactNode;
20
+ selected?: boolean;
21
+ onSelect?: (next: boolean) => void;
22
+ /** Radio group name (single-select). */
23
+ name?: string;
24
+ value?: string;
25
+ /** Pick-many: square marker + checkbox input. */
26
+ multi?: boolean;
27
+ disabled?: boolean;
28
+ /** Dense padding + smaller type. */
29
+ size?: 'md' | 'sm';
30
+ /** Dark-surface treatment (hero, permanently-dark onboarding). */
31
+ onInverse?: boolean;
32
+ className?: string;
33
+ style?: React.CSSProperties;
34
+ }
35
+
36
+ export const SelectableCard: React.FC<SelectableCardProps> = ({
37
+ title,
38
+ desc,
39
+ index,
40
+ selected = false,
41
+ onSelect,
42
+ name,
43
+ value,
44
+ multi = false,
45
+ disabled = false,
46
+ size = 'md',
47
+ onInverse = false,
48
+ className,
49
+ style,
50
+ }) => {
51
+ const cls = [
52
+ 'sel-card',
53
+ size === 'sm' ? 'sel-card-sm' : '',
54
+ multi ? 'sel-card-multi' : '',
55
+ selected ? 'is-selected' : '',
56
+ onInverse ? 'on-inverse' : '',
57
+ className || '',
58
+ ].filter(Boolean).join(' ');
59
+
60
+ return (
61
+ <label className={cls} style={style} aria-disabled={disabled || undefined}>
62
+ <input
63
+ type={multi ? 'checkbox' : 'radio'}
64
+ name={name}
65
+ value={value}
66
+ checked={selected}
67
+ disabled={disabled}
68
+ onChange={(e) => onSelect?.(e.target.checked)}
69
+ />
70
+ {index != null ? <span className="sel-card-index">{index}</span> : null}
71
+ <span className="sel-card-body">
72
+ <span className="sel-card-title">{title}</span>
73
+ {desc != null ? <span className="sel-card-desc">{desc}</span> : null}
74
+ </span>
75
+ <span className="sel-card-mark" aria-hidden="true" />
76
+ </label>
77
+ );
78
+ };
79
+
80
+ export interface SelectableCardGroupProps {
81
+ children?: React.ReactNode;
82
+ /** Auto-fit grid instead of a single column. */
83
+ grid?: boolean;
84
+ className?: string;
85
+ style?: React.CSSProperties;
86
+ }
87
+
88
+ export const SelectableCardGroup: React.FC<SelectableCardGroupProps> = ({ children, grid = false, className, style }) => (
89
+ <div className={['sel-card-group', grid ? 'is-grid' : '', className || ''].filter(Boolean).join(' ')} style={style} role="group">
90
+ {children}
91
+ </div>
92
+ );
93
+
94
+ export default SelectableCard;
@@ -150,7 +150,7 @@ export const SmartSearch: React.FC<SmartSearchProps> = ({
150
150
  {it.sub != null ? <span className="meta">{it.sub}</span> : null}
151
151
  </span>
152
152
  {it.trailing != null ? (
153
- <span style={{ marginLeft: 'auto', flexShrink: 0, fontFamily: 'var(--font-mono)', fontSize: 'var(--fs-xxs)', color: 'var(--color-foreground-muted)' }}>{it.trailing}</span>
153
+ <span style={{ marginLeft: 'auto', flexShrink: 0, fontFamily: 'var(--font-mono)', fontSizeAdjust: 'var(--font-mono-adjust)', fontSize: 'var(--fs-xxs)', color: 'var(--color-foreground-muted)' }}>{it.trailing}</span>
154
154
  ) : null}
155
155
  </li>
156
156
  );
@@ -209,7 +209,7 @@ export const SmartSearch: React.FC<SmartSearchProps> = ({
209
209
  <button key={b.key} type="button" className="ss-typebtn" onClick={() => onTypeSelect && onTypeSelect(b.key)}>
210
210
  {b.icon}
211
211
  <span className="grow">{b.label}</span>
212
- {b.hint != null ? <span style={{ fontFamily: 'var(--font-mono)', fontSize: 'var(--fs-xxs)', color: 'var(--color-foreground-muted)' }}>{b.hint}</span> : null}
212
+ {b.hint != null ? <span style={{ fontFamily: 'var(--font-mono)', fontSizeAdjust: 'var(--font-mono-adjust)', fontSize: 'var(--fs-xxs)', color: 'var(--color-foreground-muted)' }}>{b.hint}</span> : null}
213
213
  </button>
214
214
  ))}
215
215
  </div>
@@ -13,6 +13,9 @@ export interface StepperProps extends React.OlHTMLAttributes<HTMLOListElement> {
13
13
  steps: StepItem[];
14
14
  vertical?: boolean;
15
15
  compact?: boolean;
16
+ /** Type scale for the label + helper, independent of marker density.
17
+ Composes with `compact`: geometry and type are separate axes. */
18
+ labelSize?: 'sm' | 'md' | 'lg';
16
19
  /** Minimal capsule+label variant (.is-rule) for tight headers. */
17
20
  rule?: boolean;
18
21
  /** ≤640px: hide labels, keep markers (.is-collapse-sm). */
@@ -5,6 +5,8 @@ import * as React from 'react';
5
5
  * indicator (preview/components/stepper.css). States: todo / current /
6
6
  * done / error / skipped. Orientations: horizontal (default), vertical.
7
7
  * Density: compact. Minimal: rule (4px capsule, mono labels).
8
+ * Label type scale: labelSize sm | md | lg — a separate axis from density, so a
9
+ * compact stepper can still carry a large label.
8
10
  */
9
11
 
10
12
  export type StepState = 'todo' | 'current' | 'done' | 'error' | 'skipped';
@@ -20,6 +22,9 @@ export interface StepperProps extends React.OlHTMLAttributes<HTMLOListElement> {
20
22
  steps: StepItem[];
21
23
  vertical?: boolean;
22
24
  compact?: boolean;
25
+ /** Type scale for the label + helper, independent of marker density.
26
+ Composes with `compact`: geometry and type are separate axes. */
27
+ labelSize?: 'sm' | 'md' | 'lg';
23
28
  /** Minimal capsule+label variant (.is-rule) for tight headers. */
24
29
  rule?: boolean;
25
30
  /** ≤640px: hide labels, keep markers (.is-collapse-sm). */
@@ -44,6 +49,7 @@ export const Stepper: React.FC<StepperProps> = ({
44
49
  steps,
45
50
  vertical = false,
46
51
  compact = false,
52
+ labelSize,
47
53
  rule = false,
48
54
  collapseOnMobile = false,
49
55
  onStepClick,
@@ -54,6 +60,7 @@ export const Stepper: React.FC<StepperProps> = ({
54
60
  'stepper',
55
61
  vertical ? 'is-vertical' : '',
56
62
  compact ? 'is-compact' : '',
63
+ labelSize ? 'stepper-label-' + labelSize : '',
57
64
  rule ? 'is-rule' : '',
58
65
  collapseOnMobile ? 'is-collapse-sm' : '',
59
66
  className || '',
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  export type ToastTone = 'success' | 'warning' | 'danger' | 'info' | 'brand' | 'inverse';
4
4
  export type ToastStackPosition = 'tr' | 'tl' | 'tc' | 'br' | 'bl' | 'bc';
5
5
 
6
- export interface ToastProps extends React.HTMLAttributes<HTMLDivElement> {
6
+ export interface ToastProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
7
7
  tone?: ToastTone;
8
8
  /** Leading icon slot (.toast-icon). */
9
9
  icon?: React.ReactNode;
@@ -10,7 +10,7 @@ import * as React from 'react';
10
10
  export type ToastTone = 'success' | 'warning' | 'danger' | 'info' | 'brand' | 'inverse';
11
11
  export type ToastStackPosition = 'tr' | 'tl' | 'tc' | 'br' | 'bl' | 'bc';
12
12
 
13
- export interface ToastProps extends React.HTMLAttributes<HTMLDivElement> {
13
+ export interface ToastProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
14
14
  tone?: ToastTone;
15
15
  /** Leading icon slot (.toast-icon). */
16
16
  icon?: React.ReactNode;
@@ -0,0 +1,29 @@
1
+ import * as React from 'react';
2
+
3
+ export interface ToggleChipProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onChange'> {
4
+ children?: React.ReactNode;
5
+ /** Leading icon. */
6
+ icon?: React.ReactNode;
7
+ /** Trailing count. */
8
+ count?: React.ReactNode;
9
+ /** On state (controlled) — drives aria-pressed. */
10
+ pressed?: boolean;
11
+ onToggle?: (next: boolean) => void;
12
+ disabled?: boolean;
13
+ size?: 'md' | 'sm';
14
+ /** Dark-surface treatment. */
15
+ onInverse?: boolean;
16
+ }
17
+
18
+ export declare const ToggleChip: React.FC<ToggleChipProps>;
19
+
20
+ export interface ToggleChipGroupProps {
21
+ children?: React.ReactNode;
22
+ className?: string;
23
+ style?: React.CSSProperties;
24
+ 'aria-label'?: string;
25
+ }
26
+
27
+ export declare const ToggleChipGroup: React.FC<ToggleChipGroupProps>;
28
+
29
+ export default ToggleChip;
@@ -0,0 +1,76 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * ATTRUS ToggleChip — typed wrapper over the canonical `.toggle-chip`
5
+ * (preview/components/toggle-chip.css): a chip that is a FORM CONTROL.
6
+ *
7
+ * Use for multi-select filters, tag pickers and preference pills. Do NOT use
8
+ * `.chip` (that is a removable token, it ships `.chip-x`) or `.pill.interactive`
9
+ * (that is a status pill) for selection.
10
+ *
11
+ * Renders a `<button aria-pressed>` by default — the accessible pattern for a
12
+ * toggle. Provide `name` to render a hidden checkbox instead (form submission).
13
+ */
14
+ export interface ToggleChipProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onChange'> {
15
+ children?: React.ReactNode;
16
+ /** Leading icon. */
17
+ icon?: React.ReactNode;
18
+ /** Trailing count. */
19
+ count?: React.ReactNode;
20
+ pressed?: boolean;
21
+ onToggle?: (next: boolean) => void;
22
+ disabled?: boolean;
23
+ size?: 'md' | 'sm';
24
+ /** Dark-surface treatment. */
25
+ onInverse?: boolean;
26
+ }
27
+
28
+ export const ToggleChip: React.FC<ToggleChipProps> = ({
29
+ children,
30
+ icon,
31
+ count,
32
+ pressed = false,
33
+ onToggle,
34
+ disabled = false,
35
+ size = 'md',
36
+ onInverse = false,
37
+ className,
38
+ ...rest
39
+ }) => {
40
+ const cls = [
41
+ 'toggle-chip',
42
+ size === 'sm' ? 'toggle-chip-sm' : '',
43
+ onInverse ? 'on-inverse' : '',
44
+ className || '',
45
+ ].filter(Boolean).join(' ');
46
+
47
+ return (
48
+ <button
49
+ type="button"
50
+ className={cls}
51
+ aria-pressed={pressed}
52
+ disabled={disabled}
53
+ onClick={() => onToggle?.(!pressed)}
54
+ {...rest}
55
+ >
56
+ {icon}
57
+ {children}
58
+ {count != null ? <span className="toggle-chip-count">{count}</span> : null}
59
+ </button>
60
+ );
61
+ };
62
+
63
+ export interface ToggleChipGroupProps {
64
+ children?: React.ReactNode;
65
+ className?: string;
66
+ style?: React.CSSProperties;
67
+ 'aria-label'?: string;
68
+ }
69
+
70
+ export const ToggleChipGroup: React.FC<ToggleChipGroupProps> = ({ children, className, ...rest }) => (
71
+ <div className={['toggle-chip-group', className || ''].filter(Boolean).join(' ')} role="group" {...rest}>
72
+ {children}
73
+ </div>
74
+ );
75
+
76
+ export default ToggleChip;
@@ -2,7 +2,7 @@ import * as React from 'react';
2
2
 
3
3
  export type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
4
4
 
5
- export interface TooltipProps extends React.HTMLAttributes<HTMLSpanElement> {
5
+ export interface TooltipProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'content'> {
6
6
  /** Tooltip content — plain label, or rich blocks when rich=true. */
7
7
  content: React.ReactNode;
8
8
  placement?: TooltipPlacement;
@@ -9,7 +9,7 @@ import * as React from 'react';
9
9
 
10
10
  export type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
11
11
 
12
- export interface TooltipProps extends React.HTMLAttributes<HTMLSpanElement> {
12
+ export interface TooltipProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'content'> {
13
13
  /** Tooltip content — plain label, or rich blocks when rich=true. */
14
14
  content: React.ReactNode;
15
15
  placement?: TooltipPlacement;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@attrus-ds/core",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "ATTRUS / Cabiros design system — design tokens, component styles, brand assets, fonts and an optional typed React component layer.",
5
5
  "keywords": [
6
6
  "design-system",