@attrus-ds/core 0.2.0 → 1.0.0
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/LICENSE +27 -27
- package/dist/assets/flags/Brazil-rounded.svg +15 -15
- package/dist/assets/flags/Brazil-square.svg +16 -16
- package/dist/assets/flags/Chile-rounded.svg +10 -10
- package/dist/assets/flags/Chile-square.svg +10 -10
- package/dist/assets/flags/Colombia-rounded.svg +9 -9
- package/dist/assets/flags/Colombia-square.svg +9 -9
- package/dist/assets/flags/EUA-rounded.svg +10 -10
- package/dist/assets/flags/EUA-square.svg +10 -10
- package/dist/assets/flags/EUR-rounded.svg +8 -8
- package/dist/assets/flags/EUR-square.svg +8 -8
- package/dist/assets/flags/Mexico-rounded.svg +18 -18
- package/dist/assets/flags/Mexico-square.svg +18 -18
- package/dist/assets/icon-attrus-dark.svg +2 -2
- package/dist/assets/icon-attrus-light.svg +2 -2
- package/dist/assets/logo-attrus-dark.svg +7 -7
- package/dist/assets/logo-attrus-light.svg +7 -7
- package/dist/css/components/buttons.css +301 -301
- package/dist/css/components/callout.css +195 -195
- package/dist/css/components/cards.css +272 -272
- package/dist/css/components/combobox.css +319 -319
- package/dist/css/components/empty-states.css +61 -61
- package/dist/css/components/inputs.css +975 -975
- package/dist/css/components/number-input.css +138 -138
- package/dist/css/components/overlays.css +374 -374
- package/dist/css/components/primitives.css +314 -314
- package/dist/css/components/sidebar.css +179 -179
- package/dist/css/components/smart-search.css +91 -91
- package/dist/css/components/status-pills.css +207 -207
- package/dist/css/components/stepper.css +309 -309
- package/dist/css/components/table.css +602 -602
- package/dist/css/components/tabs.css +319 -319
- package/dist/css/components/toast.css +243 -243
- package/dist/css/components/tooltip.css +231 -231
- package/dist/css/components/topbar.css +148 -148
- package/dist/css/tokens.css +713 -666
- package/dist/react/AppSidebar/AppSidebar.d.ts +50 -50
- package/dist/react/AppSidebar/AppSidebar.tsx +197 -197
- package/dist/react/AppTopbar/AppTopbar.d.ts +58 -58
- package/dist/react/AppTopbar/AppTopbar.tsx +203 -203
- package/dist/react/Atoms/Atoms.d.ts +149 -149
- package/dist/react/Atoms/Atoms.tsx +407 -407
- package/dist/react/Button/Button.d.ts +25 -25
- package/dist/react/Button/Button.tsx +83 -83
- package/dist/react/Callout/Callout.d.ts +29 -29
- package/dist/react/Callout/Callout.tsx +98 -98
- package/dist/react/Card/Card.d.ts +38 -38
- package/dist/react/Card/Card.tsx +93 -93
- package/dist/react/Combobox/Combobox.d.ts +52 -52
- package/dist/react/Combobox/Combobox.tsx +254 -254
- package/dist/react/DataTable/DataTable.d.ts +81 -81
- package/dist/react/DataTable/DataTable.tsx +308 -308
- package/dist/react/Drawer/Drawer.d.ts +35 -35
- package/dist/react/Drawer/Drawer.tsx +226 -226
- package/dist/react/Input/Input.d.ts +155 -155
- package/dist/react/Input/Input.tsx +478 -478
- package/dist/react/Modal/Modal.d.ts +27 -27
- package/dist/react/Modal/Modal.tsx +173 -173
- package/dist/react/NumberInput/NumberInput.d.ts +25 -25
- package/dist/react/NumberInput/NumberInput.tsx +117 -117
- package/dist/react/Pill/Pill.d.ts +31 -31
- package/dist/react/Pill/Pill.tsx +104 -104
- package/dist/react/SearchPill/SearchPill.d.ts +14 -14
- package/dist/react/SearchPill/SearchPill.tsx +40 -40
- package/dist/react/SmartSearch/SmartSearch.d.ts +65 -65
- package/dist/react/SmartSearch/SmartSearch.tsx +271 -271
- package/dist/react/Stepper/Stepper.d.ts +25 -25
- package/dist/react/Stepper/Stepper.tsx +94 -94
- package/dist/react/Tabs/Tabs.d.ts +32 -32
- package/dist/react/Tabs/Tabs.tsx +147 -147
- package/dist/react/Toast/Toast.d.ts +30 -30
- package/dist/react/Toast/Toast.tsx +94 -94
- package/dist/react/Tooltip/Tooltip.d.ts +23 -23
- package/dist/react/Tooltip/Tooltip.tsx +80 -80
- package/dist/react/ZeroState/ZeroState.d.ts +17 -17
- package/dist/react/ZeroState/ZeroState.tsx +45 -45
- package/dist/react/index.ts +35 -35
- package/dist/tokens/attrus-tokens.flat.json +1234 -1234
- package/dist/tokens/attrus-tokens.json +1089 -1089
- package/package.json +66 -64
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* ATTRUS NumberInput — typed wrapper over the canonical `.number-input`
|
|
5
|
-
* (preview/components/number-input.css): − / + accelerator buttons
|
|
6
|
-
* around a numeric field. For bounded quantities (counts, %, days).
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export type NumberInputSize = 'sm' | 'md' | 'lg';
|
|
10
|
-
|
|
11
|
-
export interface NumberInputProps {
|
|
12
|
-
value: number;
|
|
13
|
-
onChange?: (value: number) => void;
|
|
14
|
-
min?: number;
|
|
15
|
-
max?: number;
|
|
16
|
-
step?: number;
|
|
17
|
-
size?: NumberInputSize;
|
|
18
|
-
/** Static cell before the field (e.g. "$"). */
|
|
19
|
-
prefix?: React.ReactNode;
|
|
20
|
-
/** Static cell after the field (e.g. "%", "days"). */
|
|
21
|
-
suffix?: React.ReactNode;
|
|
22
|
-
disabled?: boolean;
|
|
23
|
-
invalid?: boolean;
|
|
24
|
-
'aria-label'?: string;
|
|
25
|
-
id?: string;
|
|
26
|
-
className?: string;
|
|
27
|
-
style?: React.CSSProperties;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const Minus: React.FC = () => (
|
|
31
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.4} strokeLinecap="round" aria-hidden="true">
|
|
32
|
-
<line x1="5" y1="12" x2="19" y2="12" />
|
|
33
|
-
</svg>
|
|
34
|
-
);
|
|
35
|
-
const Plus: React.FC = () => (
|
|
36
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.4} strokeLinecap="round" aria-hidden="true">
|
|
37
|
-
<line x1="12" y1="5" x2="12" y2="19" />
|
|
38
|
-
<line x1="5" y1="12" x2="19" y2="12" />
|
|
39
|
-
</svg>
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
function clamp(v: number, min?: number, max?: number): number {
|
|
43
|
-
if (min != null && v < min) return min;
|
|
44
|
-
if (max != null && v > max) return max;
|
|
45
|
-
return v;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export const NumberInput: React.FC<NumberInputProps> = ({
|
|
49
|
-
value,
|
|
50
|
-
onChange,
|
|
51
|
-
min,
|
|
52
|
-
max,
|
|
53
|
-
step = 1,
|
|
54
|
-
size = 'md',
|
|
55
|
-
prefix,
|
|
56
|
-
suffix,
|
|
57
|
-
disabled = false,
|
|
58
|
-
invalid = false,
|
|
59
|
-
className,
|
|
60
|
-
style,
|
|
61
|
-
...rest
|
|
62
|
-
}) => {
|
|
63
|
-
const cls = [
|
|
64
|
-
'number-input',
|
|
65
|
-
size !== 'md' ? `number-input-${size}` : '',
|
|
66
|
-
disabled ? 'is-disabled' : '',
|
|
67
|
-
invalid ? 'is-invalid' : '',
|
|
68
|
-
className || '',
|
|
69
|
-
]
|
|
70
|
-
.filter(Boolean)
|
|
71
|
-
.join(' ');
|
|
72
|
-
|
|
73
|
-
const set = (v: number) => {
|
|
74
|
-
if (!disabled && onChange) onChange(clamp(v, min, max));
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
return (
|
|
78
|
-
<div className={cls} style={style}>
|
|
79
|
-
<button
|
|
80
|
-
type="button"
|
|
81
|
-
className="number-input-btn"
|
|
82
|
-
aria-label="Decrease"
|
|
83
|
-
disabled={disabled || (min != null && value <= min)}
|
|
84
|
-
onClick={() => set(value - step)}
|
|
85
|
-
>
|
|
86
|
-
<Minus />
|
|
87
|
-
</button>
|
|
88
|
-
{prefix != null ? <span className="number-input-prefix">{prefix}</span> : null}
|
|
89
|
-
<input
|
|
90
|
-
type="number"
|
|
91
|
-
className="number-input-field"
|
|
92
|
-
value={value}
|
|
93
|
-
min={min}
|
|
94
|
-
max={max}
|
|
95
|
-
step={step}
|
|
96
|
-
disabled={disabled}
|
|
97
|
-
onChange={(e) => {
|
|
98
|
-
const v = parseFloat(e.target.value);
|
|
99
|
-
if (!isNaN(v)) set(v);
|
|
100
|
-
}}
|
|
101
|
-
{...rest}
|
|
102
|
-
/>
|
|
103
|
-
{suffix != null ? <span className="number-input-suffix">{suffix}</span> : null}
|
|
104
|
-
<button
|
|
105
|
-
type="button"
|
|
106
|
-
className="number-input-btn"
|
|
107
|
-
aria-label="Increase"
|
|
108
|
-
disabled={disabled || (max != null && value >= max)}
|
|
109
|
-
onClick={() => set(value + step)}
|
|
110
|
-
>
|
|
111
|
-
<Plus />
|
|
112
|
-
</button>
|
|
113
|
-
</div>
|
|
114
|
-
);
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
export default NumberInput;
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ATTRUS NumberInput — typed wrapper over the canonical `.number-input`
|
|
5
|
+
* (preview/components/number-input.css): − / + accelerator buttons
|
|
6
|
+
* around a numeric field. For bounded quantities (counts, %, days).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type NumberInputSize = 'sm' | 'md' | 'lg';
|
|
10
|
+
|
|
11
|
+
export interface NumberInputProps {
|
|
12
|
+
value: number;
|
|
13
|
+
onChange?: (value: number) => void;
|
|
14
|
+
min?: number;
|
|
15
|
+
max?: number;
|
|
16
|
+
step?: number;
|
|
17
|
+
size?: NumberInputSize;
|
|
18
|
+
/** Static cell before the field (e.g. "$"). */
|
|
19
|
+
prefix?: React.ReactNode;
|
|
20
|
+
/** Static cell after the field (e.g. "%", "days"). */
|
|
21
|
+
suffix?: React.ReactNode;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
invalid?: boolean;
|
|
24
|
+
'aria-label'?: string;
|
|
25
|
+
id?: string;
|
|
26
|
+
className?: string;
|
|
27
|
+
style?: React.CSSProperties;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const Minus: React.FC = () => (
|
|
31
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.4} strokeLinecap="round" aria-hidden="true">
|
|
32
|
+
<line x1="5" y1="12" x2="19" y2="12" />
|
|
33
|
+
</svg>
|
|
34
|
+
);
|
|
35
|
+
const Plus: React.FC = () => (
|
|
36
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.4} strokeLinecap="round" aria-hidden="true">
|
|
37
|
+
<line x1="12" y1="5" x2="12" y2="19" />
|
|
38
|
+
<line x1="5" y1="12" x2="19" y2="12" />
|
|
39
|
+
</svg>
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
function clamp(v: number, min?: number, max?: number): number {
|
|
43
|
+
if (min != null && v < min) return min;
|
|
44
|
+
if (max != null && v > max) return max;
|
|
45
|
+
return v;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const NumberInput: React.FC<NumberInputProps> = ({
|
|
49
|
+
value,
|
|
50
|
+
onChange,
|
|
51
|
+
min,
|
|
52
|
+
max,
|
|
53
|
+
step = 1,
|
|
54
|
+
size = 'md',
|
|
55
|
+
prefix,
|
|
56
|
+
suffix,
|
|
57
|
+
disabled = false,
|
|
58
|
+
invalid = false,
|
|
59
|
+
className,
|
|
60
|
+
style,
|
|
61
|
+
...rest
|
|
62
|
+
}) => {
|
|
63
|
+
const cls = [
|
|
64
|
+
'number-input',
|
|
65
|
+
size !== 'md' ? `number-input-${size}` : '',
|
|
66
|
+
disabled ? 'is-disabled' : '',
|
|
67
|
+
invalid ? 'is-invalid' : '',
|
|
68
|
+
className || '',
|
|
69
|
+
]
|
|
70
|
+
.filter(Boolean)
|
|
71
|
+
.join(' ');
|
|
72
|
+
|
|
73
|
+
const set = (v: number) => {
|
|
74
|
+
if (!disabled && onChange) onChange(clamp(v, min, max));
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div className={cls} style={style}>
|
|
79
|
+
<button
|
|
80
|
+
type="button"
|
|
81
|
+
className="number-input-btn"
|
|
82
|
+
aria-label="Decrease"
|
|
83
|
+
disabled={disabled || (min != null && value <= min)}
|
|
84
|
+
onClick={() => set(value - step)}
|
|
85
|
+
>
|
|
86
|
+
<Minus />
|
|
87
|
+
</button>
|
|
88
|
+
{prefix != null ? <span className="number-input-prefix">{prefix}</span> : null}
|
|
89
|
+
<input
|
|
90
|
+
type="number"
|
|
91
|
+
className="number-input-field"
|
|
92
|
+
value={value}
|
|
93
|
+
min={min}
|
|
94
|
+
max={max}
|
|
95
|
+
step={step}
|
|
96
|
+
disabled={disabled}
|
|
97
|
+
onChange={(e) => {
|
|
98
|
+
const v = parseFloat(e.target.value);
|
|
99
|
+
if (!isNaN(v)) set(v);
|
|
100
|
+
}}
|
|
101
|
+
{...rest}
|
|
102
|
+
/>
|
|
103
|
+
{suffix != null ? <span className="number-input-suffix">{suffix}</span> : null}
|
|
104
|
+
<button
|
|
105
|
+
type="button"
|
|
106
|
+
className="number-input-btn"
|
|
107
|
+
aria-label="Increase"
|
|
108
|
+
disabled={disabled || (max != null && value >= max)}
|
|
109
|
+
onClick={() => set(value + step)}
|
|
110
|
+
>
|
|
111
|
+
<Plus />
|
|
112
|
+
</button>
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export default NumberInput;
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
|
|
3
|
-
export type PillTone = 'success' | 'warning' | 'danger' | 'info' | 'neutral' | 'brand';
|
|
4
|
-
export type PillSize = 'sm' | 'md' | 'lg';
|
|
5
|
-
export type PillAppearance = 'soft' | 'solid' | 'outline';
|
|
6
|
-
|
|
7
|
-
export interface PillProps extends React.HTMLAttributes<HTMLElement> {
|
|
8
|
-
/** Semantic tone. Variant carries meaning; label is required. Default: 'neutral'. */
|
|
9
|
-
tone?: PillTone;
|
|
10
|
-
/** sm=20px, md=24px (default), lg=28px. */
|
|
11
|
-
size?: PillSize;
|
|
12
|
-
/** soft (tinted, default) | solid (filled) | outline (transparent bg). */
|
|
13
|
-
appearance?: PillAppearance;
|
|
14
|
-
/** Leading status dot (decorative — meaning lives in label + tone). */
|
|
15
|
-
dot?: boolean;
|
|
16
|
-
/** Leading icon slot. */
|
|
17
|
-
icon?: React.ReactNode;
|
|
18
|
-
/** Renders a <button> with hover/focus/selected states (filter chips). */
|
|
19
|
-
interactive?: boolean;
|
|
20
|
-
/** Selected state for interactive pills (sets aria-pressed). */
|
|
21
|
-
selected?: boolean;
|
|
22
|
-
/** Glassy translucent treatment for dark surfaces. */
|
|
23
|
-
onInverse?: boolean;
|
|
24
|
-
/** Renders the trailing close (×) — `.pill-x` — and calls back on click (removable filter chips). */
|
|
25
|
-
onRemove?: (e: React.MouseEvent) => void;
|
|
26
|
-
/** Dimmed, non-interactive. */
|
|
27
|
-
disabled?: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export declare const Pill: React.FC<PillProps>;
|
|
31
|
-
export default Pill;
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
export type PillTone = 'success' | 'warning' | 'danger' | 'info' | 'neutral' | 'brand';
|
|
4
|
+
export type PillSize = 'sm' | 'md' | 'lg';
|
|
5
|
+
export type PillAppearance = 'soft' | 'solid' | 'outline';
|
|
6
|
+
|
|
7
|
+
export interface PillProps extends React.HTMLAttributes<HTMLElement> {
|
|
8
|
+
/** Semantic tone. Variant carries meaning; label is required. Default: 'neutral'. */
|
|
9
|
+
tone?: PillTone;
|
|
10
|
+
/** sm=20px, md=24px (default), lg=28px. */
|
|
11
|
+
size?: PillSize;
|
|
12
|
+
/** soft (tinted, default) | solid (filled) | outline (transparent bg). */
|
|
13
|
+
appearance?: PillAppearance;
|
|
14
|
+
/** Leading status dot (decorative — meaning lives in label + tone). */
|
|
15
|
+
dot?: boolean;
|
|
16
|
+
/** Leading icon slot. */
|
|
17
|
+
icon?: React.ReactNode;
|
|
18
|
+
/** Renders a <button> with hover/focus/selected states (filter chips). */
|
|
19
|
+
interactive?: boolean;
|
|
20
|
+
/** Selected state for interactive pills (sets aria-pressed). */
|
|
21
|
+
selected?: boolean;
|
|
22
|
+
/** Glassy translucent treatment for dark surfaces. */
|
|
23
|
+
onInverse?: boolean;
|
|
24
|
+
/** Renders the trailing close (×) — `.pill-x` — and calls back on click (removable filter chips). */
|
|
25
|
+
onRemove?: (e: React.MouseEvent) => void;
|
|
26
|
+
/** Dimmed, non-interactive. */
|
|
27
|
+
disabled?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export declare const Pill: React.FC<PillProps>;
|
|
31
|
+
export default Pill;
|
package/dist/react/Pill/Pill.tsx
CHANGED
|
@@ -1,104 +1,104 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* ATTRUS Pill — typed wrapper over the canonical `.pill` system
|
|
5
|
-
* (preview/components/status-pills.css). CSS stays the visual source
|
|
6
|
-
* of truth; this component only composes class names.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export type PillTone = 'success' | 'warning' | 'danger' | 'info' | 'neutral' | 'brand';
|
|
10
|
-
export type PillSize = 'sm' | 'md' | 'lg';
|
|
11
|
-
export type PillAppearance = 'soft' | 'solid' | 'outline';
|
|
12
|
-
|
|
13
|
-
export interface PillProps extends React.HTMLAttributes<HTMLElement> {
|
|
14
|
-
/** Semantic tone. Variant carries meaning; label is required. Default: 'neutral'. */
|
|
15
|
-
tone?: PillTone;
|
|
16
|
-
/** sm=20px, md=24px (default), lg=28px. */
|
|
17
|
-
size?: PillSize;
|
|
18
|
-
/** soft (tinted, default) | solid (filled) | outline (transparent bg). */
|
|
19
|
-
appearance?: PillAppearance;
|
|
20
|
-
/** Leading status dot (decorative — meaning lives in label + tone). */
|
|
21
|
-
dot?: boolean;
|
|
22
|
-
/** Leading icon slot. */
|
|
23
|
-
icon?: React.ReactNode;
|
|
24
|
-
/** Renders a <button> with hover/focus/selected states (filter chips). */
|
|
25
|
-
interactive?: boolean;
|
|
26
|
-
/** Selected state for interactive pills (sets aria-pressed). */
|
|
27
|
-
selected?: boolean;
|
|
28
|
-
/** Glassy translucent treatment for dark surfaces. */
|
|
29
|
-
onInverse?: boolean;
|
|
30
|
-
/** Renders the trailing close (×) — `.pill-x` — and calls back on click (removable filter chips). */
|
|
31
|
-
onRemove?: (e: React.MouseEvent) => void;
|
|
32
|
-
/** Dimmed, non-interactive. */
|
|
33
|
-
disabled?: boolean;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export const Pill: React.FC<PillProps> = ({
|
|
37
|
-
tone = 'neutral',
|
|
38
|
-
size = 'md',
|
|
39
|
-
appearance = 'soft',
|
|
40
|
-
dot = false,
|
|
41
|
-
icon,
|
|
42
|
-
interactive = false,
|
|
43
|
-
selected = false,
|
|
44
|
-
onInverse = false,
|
|
45
|
-
onRemove,
|
|
46
|
-
disabled = false,
|
|
47
|
-
className,
|
|
48
|
-
children,
|
|
49
|
-
...rest
|
|
50
|
-
}) => {
|
|
51
|
-
const cls = [
|
|
52
|
-
'pill',
|
|
53
|
-
tone,
|
|
54
|
-
size !== 'md' ? size : '',
|
|
55
|
-
appearance !== 'soft' ? appearance : '',
|
|
56
|
-
interactive ? 'interactive' : '',
|
|
57
|
-
onInverse ? 'on-inverse' : '',
|
|
58
|
-
className || '',
|
|
59
|
-
]
|
|
60
|
-
.filter(Boolean)
|
|
61
|
-
.join(' ');
|
|
62
|
-
|
|
63
|
-
const content = (
|
|
64
|
-
<React.Fragment>
|
|
65
|
-
{dot ? <span className="dot" /> : null}
|
|
66
|
-
{icon}
|
|
67
|
-
{children != null ? <span className="pill-label">{children}</span> : null}
|
|
68
|
-
{onRemove ? (
|
|
69
|
-
<span
|
|
70
|
-
className="pill-x"
|
|
71
|
-
role="button"
|
|
72
|
-
tabIndex={0}
|
|
73
|
-
aria-label="Remove"
|
|
74
|
-
onClick={(e) => { e.stopPropagation(); onRemove(e); }}
|
|
75
|
-
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); e.stopPropagation(); onRemove(e as unknown as React.MouseEvent); } }}
|
|
76
|
-
>
|
|
77
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round"><line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/></svg>
|
|
78
|
-
</span>
|
|
79
|
-
) : null}
|
|
80
|
-
</React.Fragment>
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
if (interactive) {
|
|
84
|
-
return (
|
|
85
|
-
<button
|
|
86
|
-
type="button"
|
|
87
|
-
className={cls}
|
|
88
|
-
aria-pressed={selected}
|
|
89
|
-
disabled={disabled}
|
|
90
|
-
{...(rest as React.ButtonHTMLAttributes<HTMLButtonElement>)}
|
|
91
|
-
>
|
|
92
|
-
{content}
|
|
93
|
-
</button>
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return (
|
|
98
|
-
<span className={cls} aria-disabled={disabled || undefined} {...rest}>
|
|
99
|
-
{content}
|
|
100
|
-
</span>
|
|
101
|
-
);
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
export default Pill;
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ATTRUS Pill — typed wrapper over the canonical `.pill` system
|
|
5
|
+
* (preview/components/status-pills.css). CSS stays the visual source
|
|
6
|
+
* of truth; this component only composes class names.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type PillTone = 'success' | 'warning' | 'danger' | 'info' | 'neutral' | 'brand';
|
|
10
|
+
export type PillSize = 'sm' | 'md' | 'lg';
|
|
11
|
+
export type PillAppearance = 'soft' | 'solid' | 'outline';
|
|
12
|
+
|
|
13
|
+
export interface PillProps extends React.HTMLAttributes<HTMLElement> {
|
|
14
|
+
/** Semantic tone. Variant carries meaning; label is required. Default: 'neutral'. */
|
|
15
|
+
tone?: PillTone;
|
|
16
|
+
/** sm=20px, md=24px (default), lg=28px. */
|
|
17
|
+
size?: PillSize;
|
|
18
|
+
/** soft (tinted, default) | solid (filled) | outline (transparent bg). */
|
|
19
|
+
appearance?: PillAppearance;
|
|
20
|
+
/** Leading status dot (decorative — meaning lives in label + tone). */
|
|
21
|
+
dot?: boolean;
|
|
22
|
+
/** Leading icon slot. */
|
|
23
|
+
icon?: React.ReactNode;
|
|
24
|
+
/** Renders a <button> with hover/focus/selected states (filter chips). */
|
|
25
|
+
interactive?: boolean;
|
|
26
|
+
/** Selected state for interactive pills (sets aria-pressed). */
|
|
27
|
+
selected?: boolean;
|
|
28
|
+
/** Glassy translucent treatment for dark surfaces. */
|
|
29
|
+
onInverse?: boolean;
|
|
30
|
+
/** Renders the trailing close (×) — `.pill-x` — and calls back on click (removable filter chips). */
|
|
31
|
+
onRemove?: (e: React.MouseEvent) => void;
|
|
32
|
+
/** Dimmed, non-interactive. */
|
|
33
|
+
disabled?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const Pill: React.FC<PillProps> = ({
|
|
37
|
+
tone = 'neutral',
|
|
38
|
+
size = 'md',
|
|
39
|
+
appearance = 'soft',
|
|
40
|
+
dot = false,
|
|
41
|
+
icon,
|
|
42
|
+
interactive = false,
|
|
43
|
+
selected = false,
|
|
44
|
+
onInverse = false,
|
|
45
|
+
onRemove,
|
|
46
|
+
disabled = false,
|
|
47
|
+
className,
|
|
48
|
+
children,
|
|
49
|
+
...rest
|
|
50
|
+
}) => {
|
|
51
|
+
const cls = [
|
|
52
|
+
'pill',
|
|
53
|
+
tone,
|
|
54
|
+
size !== 'md' ? size : '',
|
|
55
|
+
appearance !== 'soft' ? appearance : '',
|
|
56
|
+
interactive ? 'interactive' : '',
|
|
57
|
+
onInverse ? 'on-inverse' : '',
|
|
58
|
+
className || '',
|
|
59
|
+
]
|
|
60
|
+
.filter(Boolean)
|
|
61
|
+
.join(' ');
|
|
62
|
+
|
|
63
|
+
const content = (
|
|
64
|
+
<React.Fragment>
|
|
65
|
+
{dot ? <span className="dot" /> : null}
|
|
66
|
+
{icon}
|
|
67
|
+
{children != null ? <span className="pill-label">{children}</span> : null}
|
|
68
|
+
{onRemove ? (
|
|
69
|
+
<span
|
|
70
|
+
className="pill-x"
|
|
71
|
+
role="button"
|
|
72
|
+
tabIndex={0}
|
|
73
|
+
aria-label="Remove"
|
|
74
|
+
onClick={(e) => { e.stopPropagation(); onRemove(e); }}
|
|
75
|
+
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); e.stopPropagation(); onRemove(e as unknown as React.MouseEvent); } }}
|
|
76
|
+
>
|
|
77
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round"><line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/></svg>
|
|
78
|
+
</span>
|
|
79
|
+
) : null}
|
|
80
|
+
</React.Fragment>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (interactive) {
|
|
84
|
+
return (
|
|
85
|
+
<button
|
|
86
|
+
type="button"
|
|
87
|
+
className={cls}
|
|
88
|
+
aria-pressed={selected}
|
|
89
|
+
disabled={disabled}
|
|
90
|
+
{...(rest as React.ButtonHTMLAttributes<HTMLButtonElement>)}
|
|
91
|
+
>
|
|
92
|
+
{content}
|
|
93
|
+
</button>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<span className={cls} aria-disabled={disabled || undefined} {...rest}>
|
|
99
|
+
{content}
|
|
100
|
+
</span>
|
|
101
|
+
);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export default Pill;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
|
|
3
|
-
export interface SearchPillProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
4
|
-
/** Keyboard hint rendered as a .kbd chip (e.g. "⌘K"). */
|
|
5
|
-
shortcut?: string;
|
|
6
|
-
/** Leading icon — defaults to the canonical search glyph. */
|
|
7
|
-
icon?: React.ReactNode;
|
|
8
|
-
/** Wrapper (pill) className/style — the input itself fills it. */
|
|
9
|
-
wrapperClassName?: string;
|
|
10
|
-
wrapperStyle?: React.CSSProperties;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export declare const SearchPill: React.FC<SearchPillProps>;
|
|
14
|
-
export default SearchPill;
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
export interface SearchPillProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
4
|
+
/** Keyboard hint rendered as a .kbd chip (e.g. "⌘K"). */
|
|
5
|
+
shortcut?: string;
|
|
6
|
+
/** Leading icon — defaults to the canonical search glyph. */
|
|
7
|
+
icon?: React.ReactNode;
|
|
8
|
+
/** Wrapper (pill) className/style — the input itself fills it. */
|
|
9
|
+
wrapperClassName?: string;
|
|
10
|
+
wrapperStyle?: React.CSSProperties;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export declare const SearchPill: React.FC<SearchPillProps>;
|
|
14
|
+
export default SearchPill;
|
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* ATTRUS SearchPill — typed wrapper over the canonical `.pill-search`
|
|
5
|
-
* (preview/components/inputs.css): leading icon + borderless input +
|
|
6
|
-
* optional .kbd shortcut hint. The ⌘K-style global search bar.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export interface SearchPillProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
10
|
-
/** Keyboard hint rendered as a .kbd chip (e.g. "⌘K"). */
|
|
11
|
-
shortcut?: string;
|
|
12
|
-
/** Leading icon — defaults to the canonical search glyph. */
|
|
13
|
-
icon?: React.ReactNode;
|
|
14
|
-
/** Wrapper (pill) className/style — the input itself fills it. */
|
|
15
|
-
wrapperClassName?: string;
|
|
16
|
-
wrapperStyle?: React.CSSProperties;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const SearchGlyph: React.FC = () => (
|
|
20
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
21
|
-
<circle cx="11" cy="11" r="8" />
|
|
22
|
-
<path d="m21 21-4.3-4.3" />
|
|
23
|
-
</svg>
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
export const SearchPill: React.FC<SearchPillProps> = ({
|
|
27
|
-
shortcut,
|
|
28
|
-
icon,
|
|
29
|
-
wrapperClassName,
|
|
30
|
-
wrapperStyle,
|
|
31
|
-
...rest
|
|
32
|
-
}) => (
|
|
33
|
-
<div className={['pill-search', wrapperClassName || ''].filter(Boolean).join(' ')} style={wrapperStyle}>
|
|
34
|
-
{icon != null ? icon : <SearchGlyph />}
|
|
35
|
-
<input type="search" {...rest} />
|
|
36
|
-
{shortcut ? <span className="kbd">{shortcut}</span> : null}
|
|
37
|
-
</div>
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
export default SearchPill;
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ATTRUS SearchPill — typed wrapper over the canonical `.pill-search`
|
|
5
|
+
* (preview/components/inputs.css): leading icon + borderless input +
|
|
6
|
+
* optional .kbd shortcut hint. The ⌘K-style global search bar.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export interface SearchPillProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
10
|
+
/** Keyboard hint rendered as a .kbd chip (e.g. "⌘K"). */
|
|
11
|
+
shortcut?: string;
|
|
12
|
+
/** Leading icon — defaults to the canonical search glyph. */
|
|
13
|
+
icon?: React.ReactNode;
|
|
14
|
+
/** Wrapper (pill) className/style — the input itself fills it. */
|
|
15
|
+
wrapperClassName?: string;
|
|
16
|
+
wrapperStyle?: React.CSSProperties;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const SearchGlyph: React.FC = () => (
|
|
20
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
21
|
+
<circle cx="11" cy="11" r="8" />
|
|
22
|
+
<path d="m21 21-4.3-4.3" />
|
|
23
|
+
</svg>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export const SearchPill: React.FC<SearchPillProps> = ({
|
|
27
|
+
shortcut,
|
|
28
|
+
icon,
|
|
29
|
+
wrapperClassName,
|
|
30
|
+
wrapperStyle,
|
|
31
|
+
...rest
|
|
32
|
+
}) => (
|
|
33
|
+
<div className={['pill-search', wrapperClassName || ''].filter(Boolean).join(' ')} style={wrapperStyle}>
|
|
34
|
+
{icon != null ? icon : <SearchGlyph />}
|
|
35
|
+
<input type="search" {...rest} />
|
|
36
|
+
{shortcut ? <span className="kbd">{shortcut}</span> : null}
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export default SearchPill;
|