@gtivr4/a1-design-system-react 0.1.0 → 0.2.4

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 (111) hide show
  1. package/guidelines/Guidelines.md +228 -0
  2. package/package.json +4 -1
  3. package/src/breakpoints.css +29 -0
  4. package/src/color-scheme.css +586 -24
  5. package/src/components/accordion/Accordion.jsx +80 -0
  6. package/src/components/accordion/accordion.css +118 -0
  7. package/src/components/banner/Banner.jsx +66 -0
  8. package/src/components/banner/banner.css +205 -0
  9. package/src/components/bleed/Bleed.jsx +27 -0
  10. package/src/components/bleed/bleed.css +5 -0
  11. package/src/components/blockquote/Blockquote.jsx +40 -0
  12. package/src/components/blockquote/blockquote.css +166 -0
  13. package/src/components/breadcrumb/Breadcrumb.jsx +82 -0
  14. package/src/components/breadcrumb/breadcrumb.css +133 -0
  15. package/src/components/button/button.css +42 -12
  16. package/src/components/button-container/ButtonContainer.jsx +20 -1
  17. package/src/components/button-container/button-container.css +19 -1
  18. package/src/components/calendar/Calendar.jsx +383 -0
  19. package/src/components/calendar/calendar.css +225 -0
  20. package/src/components/card/Card.jsx +50 -12
  21. package/src/components/card/card.css +178 -14
  22. package/src/components/checkbox-group/CheckboxGroup.jsx +120 -0
  23. package/src/components/checkbox-group/checkbox-group.css +304 -0
  24. package/src/components/cluster/Cluster.jsx +52 -0
  25. package/src/components/cluster/cluster.css +9 -0
  26. package/src/components/code/Code.jsx +135 -0
  27. package/src/components/code/code.css +60 -0
  28. package/src/components/data-table/DataTable.jsx +721 -0
  29. package/src/components/data-table/DataTableFilters.jsx +339 -0
  30. package/src/components/data-table/data-table-filters.css +259 -0
  31. package/src/components/data-table/data-table.css +425 -0
  32. package/src/components/dialog/Dialog.jsx +45 -2
  33. package/src/components/dialog/dialog.css +13 -4
  34. package/src/components/divider/Divider.jsx +64 -0
  35. package/src/components/divider/divider.css +170 -0
  36. package/src/components/field/CreditCardField.jsx +131 -0
  37. package/src/components/field/DateField.jsx +11 -0
  38. package/src/components/field/NumberField.jsx +11 -0
  39. package/src/components/field/PhoneField.jsx +107 -0
  40. package/src/components/field/SelectField.jsx +86 -0
  41. package/src/components/field/TextField.jsx +83 -0
  42. package/src/components/field/TextareaField.jsx +147 -0
  43. package/src/components/field/TimeField.jsx +11 -0
  44. package/src/components/field/ZipField.jsx +114 -0
  45. package/src/components/field/credit-card.css +30 -0
  46. package/src/components/field/field.css +380 -0
  47. package/src/components/field/textarea-field.css +185 -0
  48. package/src/components/field-row/FieldRow.jsx +23 -0
  49. package/src/components/field-row/field-row.css +51 -0
  50. package/src/components/fieldset/Fieldset.jsx +49 -0
  51. package/src/components/fieldset/fieldset.css +75 -0
  52. package/src/components/figure/Figure.jsx +63 -0
  53. package/src/components/figure/figure.css +97 -0
  54. package/src/components/grid/Grid.jsx +36 -2
  55. package/src/components/grid/grid.css +129 -4
  56. package/src/components/heading/Heading.jsx +41 -1
  57. package/src/components/heading/heading.css +65 -4
  58. package/src/components/icon/icon.css +1 -0
  59. package/src/components/icon-button/icon-button.css +1 -0
  60. package/src/components/inline/inline.css +51 -0
  61. package/src/components/inline-editable/InlineEditable.jsx +77 -0
  62. package/src/components/inline-editable/inline-editable.css +47 -0
  63. package/src/components/inset/Inset.jsx +27 -0
  64. package/src/components/inset/inset.css +6 -0
  65. package/src/components/labels/Labels.jsx +5 -5
  66. package/src/components/link/Link.jsx +2 -3
  67. package/src/components/link/link.css +30 -1
  68. package/src/components/list/List.jsx +92 -0
  69. package/src/components/list/list.css +178 -0
  70. package/src/components/menu/Menu.jsx +243 -10
  71. package/src/components/menu/menu.css +157 -17
  72. package/src/components/message/Message.jsx +25 -50
  73. package/src/components/message/message.css +50 -33
  74. package/src/components/notification/Notification.jsx +1 -1
  75. package/src/components/page-layout/PageLayout.jsx +16 -1
  76. package/src/components/page-layout/page-layout.css +97 -4
  77. package/src/components/page-nav/PageNav.jsx +110 -0
  78. package/src/components/page-nav/page-nav.css +167 -0
  79. package/src/components/paragraph/Paragraph.jsx +35 -2
  80. package/src/components/paragraph/paragraph.css +38 -1
  81. package/src/components/radio-group/RadioGroup.jsx +121 -0
  82. package/src/components/radio-group/radio-group.css +268 -0
  83. package/src/components/section/Section.jsx +108 -0
  84. package/src/components/section/section.css +280 -0
  85. package/src/components/segmented-control/SegmentedControl.jsx +4 -0
  86. package/src/components/segmented-control/segmented.css +13 -0
  87. package/src/components/side-nav/SideNav.jsx +29 -9
  88. package/src/components/side-nav/scrim.css +1 -1
  89. package/src/components/side-nav/side-nav.css +70 -32
  90. package/src/components/snackbar/Snackbar.jsx +56 -0
  91. package/src/components/snackbar/snackbar.css +113 -0
  92. package/src/components/spacer/Spacer.jsx +36 -0
  93. package/src/components/spacer/spacer.css +44 -0
  94. package/src/components/stack/Stack.jsx +100 -0
  95. package/src/components/stack/stack.css +37 -0
  96. package/src/components/switch/Switch.jsx +114 -0
  97. package/src/components/switch/switch.css +276 -0
  98. package/src/components/system-banner/SystemBanner.jsx +57 -0
  99. package/src/components/system-banner/system-banner.css +118 -0
  100. package/src/components/tabs/Tabs.jsx +96 -28
  101. package/src/components/tabs/tabs.css +352 -15
  102. package/src/components/token-select/TokenSelect.jsx +159 -0
  103. package/src/components/token-select/token-select.css +110 -0
  104. package/src/components/top-header/TopHeader.jsx +641 -0
  105. package/src/components/top-header/top-header.css +337 -0
  106. package/src/illustrations/ComponentThumbnails.jsx +227 -0
  107. package/src/index.js +41 -5
  108. package/src/themes.css +256 -5
  109. package/src/tokens.css +919 -0
  110. package/src/utilities/spacing.css +8 -0
  111. package/src/utilities/sr-only.css +16 -0
@@ -0,0 +1,118 @@
1
+ /* ─── System Banner ────────────────────────────────────────────────────────── */
2
+
3
+ .a1-system-banner {
4
+ --a1-sysbanner-bg: var(--semantic-color-surface-inverse);
5
+ --a1-sysbanner-fg: var(--semantic-color-text-inverse);
6
+
7
+ background: var(--a1-sysbanner-bg);
8
+ color: var(--a1-sysbanner-fg);
9
+ width: 100%;
10
+ }
11
+
12
+ .a1-system-banner--info { --a1-sysbanner-bg: var(--semantic-color-status-info-background); }
13
+ .a1-system-banner--success { --a1-sysbanner-bg: var(--semantic-color-status-success-background); }
14
+ .a1-system-banner--warn { --a1-sysbanner-bg: var(--semantic-color-status-warn-background); }
15
+ .a1-system-banner--error { --a1-sysbanner-bg: var(--semantic-color-status-error-background); }
16
+
17
+ /* ─── Inner layout ─────────────────────────────────────────────────────────── */
18
+
19
+ .a1-system-banner__inner {
20
+ display: flex;
21
+ align-items: center;
22
+ flex-wrap: wrap;
23
+ gap: var(--base-spacing-8) var(--base-spacing-12);
24
+ max-width: var(--component-message-banner-system-max-width);
25
+ margin-inline: auto;
26
+ padding-block: var(--base-spacing-12);
27
+ padding-inline: var(--base-spacing-24);
28
+ }
29
+
30
+ /* ─── Icon ─────────────────────────────────────────────────────────────────── */
31
+
32
+ .a1-system-banner__icon {
33
+ flex-shrink: 0;
34
+ display: flex;
35
+ font-size: var(--component-message-banner-icon-size);
36
+ line-height: 1;
37
+ --a1-icon-opsz: var(--component-message-banner-icon-optical-size);
38
+ }
39
+
40
+ /* ─── Content (title + body inline) ───────────────────────────────────────── */
41
+
42
+ .a1-system-banner__content {
43
+ flex: 1;
44
+ min-width: 0;
45
+ display: flex;
46
+ flex-wrap: wrap;
47
+ align-items: baseline;
48
+ gap: 0 var(--base-spacing-8);
49
+ font-family: var(--component-paragraph-font-family);
50
+ font-size: var(--semantic-font-size-body-sm);
51
+ line-height: var(--semantic-font-line-height-body);
52
+ }
53
+
54
+ .a1-system-banner__title {
55
+ font-weight: var(--component-message-banner-title-font-weight);
56
+ color: var(--a1-sysbanner-fg);
57
+ }
58
+
59
+ .a1-system-banner__body {
60
+ font-weight: var(--semantic-font-weight-body);
61
+ color: var(--a1-sysbanner-fg);
62
+ opacity: 0.85;
63
+ }
64
+
65
+ /* ─── Action ───────────────────────────────────────────────────────────────── */
66
+
67
+ .a1-system-banner__action {
68
+ flex-shrink: 0;
69
+ }
70
+
71
+ /* ─── Dismiss ──────────────────────────────────────────────────────────────── */
72
+
73
+ .a1-system-banner__dismiss {
74
+ flex-shrink: 0;
75
+ margin-inline-start: var(--base-spacing-4);
76
+ }
77
+
78
+ /* ─── Link override on solid bg ────────────────────────────────────────────── */
79
+
80
+ .a1-system-banner .a1-link {
81
+ color: var(--a1-sysbanner-fg);
82
+ }
83
+
84
+ .a1-system-banner .a1-link:hover {
85
+ color: color-mix(in srgb, var(--a1-sysbanner-fg) 80%, transparent);
86
+ }
87
+
88
+ .a1-system-banner .a1-link:active {
89
+ color: color-mix(in srgb, var(--a1-sysbanner-fg) 65%, transparent);
90
+ }
91
+
92
+ /* ─── Tertiary button override on solid bg ─────────────────────────────────── */
93
+
94
+ .a1-system-banner .a1-button--tertiary {
95
+ --a1-button-foreground: var(--a1-sysbanner-fg);
96
+ --a1-button-foreground-hover: var(--a1-sysbanner-fg);
97
+ --a1-button-foreground-pressed: var(--a1-sysbanner-fg);
98
+ --a1-button-background: transparent;
99
+ --a1-button-background-hover: color-mix(in srgb, var(--a1-sysbanner-fg) 12%, transparent);
100
+ --a1-button-background-pressed: color-mix(in srgb, var(--a1-sysbanner-fg) 20%, transparent);
101
+ --a1-button-border: color-mix(in srgb, var(--a1-sysbanner-fg) 40%, transparent);
102
+ --a1-button-border-hover: color-mix(in srgb, var(--a1-sysbanner-fg) 60%, transparent);
103
+ --a1-button-border-pressed: color-mix(in srgb, var(--a1-sysbanner-fg) 75%, transparent);
104
+ }
105
+
106
+ /* ─── Icon button (dismiss) override on solid bg ───────────────────────────── */
107
+
108
+ .a1-system-banner .a1-icon-button {
109
+ --a1-icon-button-foreground: var(--a1-sysbanner-fg);
110
+ --a1-icon-button-foreground-hover: var(--a1-sysbanner-fg);
111
+ --a1-icon-button-foreground-pressed: var(--a1-sysbanner-fg);
112
+ --a1-icon-button-background: transparent;
113
+ --a1-icon-button-background-hover: color-mix(in srgb, var(--a1-sysbanner-fg) 12%, transparent);
114
+ --a1-icon-button-background-pressed: color-mix(in srgb, var(--a1-sysbanner-fg) 20%, transparent);
115
+ --a1-icon-button-border: transparent;
116
+ --a1-icon-button-border-hover: transparent;
117
+ --a1-icon-button-border-pressed: transparent;
118
+ }
@@ -1,21 +1,16 @@
1
- import { createContext, useContext, useId } from "react";
1
+ import { createContext, useCallback, useContext, useEffect, useId, useRef, useState } from "react";
2
+ import { Icon } from "../icon/Icon.jsx";
2
3
  import "./tabs.css";
3
4
 
4
5
  const TabsContext = createContext(null);
5
6
 
6
7
  /* ─── Tabs ─────────────────────────────────────────────────────────────────── */
7
8
 
8
- export function Tabs({
9
- children,
10
- value,
11
- onChange,
12
- variant = "line",
13
- level = 1,
14
- }) {
9
+ export function Tabs({ children, value, onChange, variant = "line", level = 1, className = "" }) {
15
10
  const uid = useId();
16
11
  return (
17
12
  <TabsContext.Provider value={{ value, onChange, variant, level, uid }}>
18
- <div className={`a1-tabs a1-tabs--level-${level}`}>
13
+ <div className={["a1-tabs", `a1-tabs--level-${level}`, className].filter(Boolean).join(" ")}>
19
14
  {children}
20
15
  </div>
21
16
  </TabsContext.Provider>
@@ -26,22 +21,50 @@ export function Tabs({
26
21
 
27
22
  export function TabList({ children }) {
28
23
  const { variant } = useContext(TabsContext);
24
+ const scrollRef = useRef(null);
25
+ const [canScrollLeft, setCanScrollLeft] = useState(false);
26
+ const [canScrollRight, setCanScrollRight] = useState(false);
27
+
28
+ // Only line tabs support overflow scroll
29
+ const enableScroll = variant === "line";
30
+
31
+ const checkScroll = useCallback(() => {
32
+ const el = scrollRef.current;
33
+ if (!el) return;
34
+ setCanScrollLeft(el.scrollLeft > 1);
35
+ setCanScrollRight(el.scrollLeft < el.scrollWidth - el.clientWidth - 1);
36
+ }, []);
37
+
38
+ useEffect(() => {
39
+ if (!enableScroll) return;
40
+ const el = scrollRef.current;
41
+ if (!el) return;
42
+ checkScroll();
43
+ el.addEventListener("scroll", checkScroll, { passive: true });
44
+ const ro = new ResizeObserver(checkScroll);
45
+ ro.observe(el);
46
+ return () => {
47
+ el.removeEventListener("scroll", checkScroll);
48
+ ro.disconnect();
49
+ };
50
+ }, [checkScroll, enableScroll]);
51
+
52
+ const scrollBy = (dir) => {
53
+ scrollRef.current?.scrollBy({ left: dir * 200, behavior: "smooth" });
54
+ };
29
55
 
30
56
  const handleKeyDown = (e) => {
31
- const tabs = Array.from(e.currentTarget.querySelectorAll('[role="tab"]:not([disabled])'));
57
+ const tabs = Array.from(
58
+ e.currentTarget.querySelectorAll('[role="tab"]:not([disabled])')
59
+ );
32
60
  const idx = tabs.indexOf(document.activeElement);
33
61
  if (idx === -1) return;
34
62
 
35
63
  let next = -1;
36
- if (e.key === "ArrowRight" || e.key === "ArrowDown") {
37
- next = (idx + 1) % tabs.length;
38
- } else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
39
- next = (idx - 1 + tabs.length) % tabs.length;
40
- } else if (e.key === "Home") {
41
- next = 0;
42
- } else if (e.key === "End") {
43
- next = tabs.length - 1;
44
- }
64
+ if (e.key === "ArrowRight" || e.key === "ArrowDown") next = (idx + 1) % tabs.length;
65
+ else if (e.key === "ArrowLeft" || e.key === "ArrowUp") next = (idx - 1 + tabs.length) % tabs.length;
66
+ else if (e.key === "Home") next = 0;
67
+ else if (e.key === "End") next = tabs.length - 1;
45
68
 
46
69
  if (next !== -1) {
47
70
  e.preventDefault();
@@ -51,22 +74,51 @@ export function TabList({ children }) {
51
74
  };
52
75
 
53
76
  return (
54
- <div
55
- role="tablist"
56
- className={`a1-tab-list a1-tab-list--${variant}`}
57
- onKeyDown={handleKeyDown}
58
- >
59
- {children}
77
+ <div className={`a1-tab-list-wrapper a1-tab-list-wrapper--${variant}`}>
78
+ {enableScroll && canScrollLeft && (
79
+ <button
80
+ type="button"
81
+ className="a1-tab-list__scroll-btn a1-tab-list__scroll-btn--prev"
82
+ onClick={() => scrollBy(-1)}
83
+ aria-label="Scroll tabs left"
84
+ tabIndex={-1}
85
+ >
86
+ <Icon name="chevron_left" />
87
+ </button>
88
+ )}
89
+ <div
90
+ ref={scrollRef}
91
+ role="tablist"
92
+ className={`a1-tab-list a1-tab-list--${variant}${enableScroll ? " a1-tab-list--scrollable" : ""}`}
93
+ onKeyDown={handleKeyDown}
94
+ >
95
+ {children}
96
+ </div>
97
+ {enableScroll && canScrollRight && (
98
+ <button
99
+ type="button"
100
+ className="a1-tab-list__scroll-btn a1-tab-list__scroll-btn--next"
101
+ onClick={() => scrollBy(1)}
102
+ aria-label="Scroll tabs right"
103
+ tabIndex={-1}
104
+ >
105
+ <Icon name="chevron_right" />
106
+ </button>
107
+ )}
60
108
  </div>
61
109
  );
62
110
  }
63
111
 
64
112
  /* ─── Tab ───────────────────────────────────────────────────────────────────── */
65
113
 
66
- export function Tab({ children, value: tabValue }) {
114
+ export function Tab({ children, value: tabValue, count, icon, iconPosition = "start", status }) {
67
115
  const { value, onChange, variant, uid } = useContext(TabsContext);
68
116
  const isSelected = value === tabValue;
69
117
 
118
+ const iconEl = icon ? (
119
+ <Icon name={icon} className="a1-tab__icon" />
120
+ ) : null;
121
+
70
122
  return (
71
123
  <button
72
124
  role="tab"
@@ -75,10 +127,26 @@ export function Tab({ children, value: tabValue }) {
75
127
  aria-selected={isSelected}
76
128
  aria-controls={`${uid}-panel-${tabValue}`}
77
129
  tabIndex={isSelected ? 0 : -1}
78
- className={`a1-tab a1-tab--${variant}`}
130
+ className={[
131
+ "a1-tab",
132
+ `a1-tab--${variant}`,
133
+ iconPosition === "above" && "a1-tab--icon-above",
134
+ status && `a1-tab--status-${status}`,
135
+ ].filter(Boolean).join(" ")}
79
136
  onClick={() => onChange?.(tabValue)}
80
137
  >
81
- {children}
138
+ {variant === "progress" && (
139
+ <span className="a1-tab__step" aria-hidden="true">
140
+ {status === "completed" && <Icon name="check" />}
141
+ </span>
142
+ )}
143
+ {iconPosition === "above" && iconEl}
144
+ {iconPosition === "start" && iconEl}
145
+ <span className="a1-tab__label">{children}</span>
146
+ {iconPosition === "end" && iconEl}
147
+ {count !== undefined && (
148
+ <span className="a1-tab__count">{count}</span>
149
+ )}
82
150
  </button>
83
151
  );
84
152
  }