@arclux/arc-ui 1.0.0 → 1.2.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/package.json +2 -2
- package/src/content/card.js +29 -2
- package/src/content/cta-banner.js +126 -0
- package/src/content/index.js +1 -0
- package/src/content/tag.js +10 -1
- package/src/index.js +1 -1
- package/src/input/button.js +46 -7
- package/src/input/form.js +120 -32
- package/src/input/input.js +67 -14
- package/src/layout/page-header.js +15 -10
- package/src/navigation/nav-item.js +2 -0
- package/src/navigation/navigation-menu.js +25 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arclux/arc-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "ARC UI — Lit Web Components implementing the Arclight design system.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
"src/",
|
|
158
158
|
"types/"
|
|
159
159
|
],
|
|
160
|
-
"sideEffects":
|
|
160
|
+
"sideEffects": true,
|
|
161
161
|
"dependencies": {
|
|
162
162
|
"lit": "^3.3.0"
|
|
163
163
|
},
|
package/src/content/card.js
CHANGED
|
@@ -4,6 +4,7 @@ import { tokenStyles } from '../shared-styles.js';
|
|
|
4
4
|
export class ArcCard extends LitElement {
|
|
5
5
|
static properties = {
|
|
6
6
|
href: { type: String },
|
|
7
|
+
_hasFooter: { state: true },
|
|
7
8
|
};
|
|
8
9
|
|
|
9
10
|
static styles = [
|
|
@@ -34,6 +35,8 @@ export class ArcCard extends LitElement {
|
|
|
34
35
|
padding: var(--space-xl) var(--space-lg);
|
|
35
36
|
flex: 1;
|
|
36
37
|
min-height: 0;
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
37
40
|
transition: box-shadow var(--transition-slow);
|
|
38
41
|
}
|
|
39
42
|
|
|
@@ -43,6 +46,18 @@ export class ArcCard extends LitElement {
|
|
|
43
46
|
|
|
44
47
|
.card:focus-visible { outline: none; box-shadow: var(--focus-glow); border-radius: var(--radius-lg); }
|
|
45
48
|
|
|
49
|
+
.card__body {
|
|
50
|
+
flex: 1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.card__footer {
|
|
54
|
+
margin-top: var(--space-md);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.card__footer--empty {
|
|
58
|
+
display: none;
|
|
59
|
+
}
|
|
60
|
+
|
|
46
61
|
@media (max-width: 768px) {
|
|
47
62
|
.card__inner { padding: var(--space-lg) var(--space-md); }
|
|
48
63
|
}
|
|
@@ -62,13 +77,25 @@ export class ArcCard extends LitElement {
|
|
|
62
77
|
constructor() {
|
|
63
78
|
super();
|
|
64
79
|
this.href = '';
|
|
80
|
+
this._hasFooter = false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_onFooterSlotChange(e) {
|
|
84
|
+
this._hasFooter = e.target.assignedNodes({ flatten: true }).length > 0;
|
|
65
85
|
}
|
|
66
86
|
|
|
67
87
|
render() {
|
|
88
|
+
const content = html`
|
|
89
|
+
<div class="card__body" part="body"><slot></slot></div>
|
|
90
|
+
<div class="card__footer ${this._hasFooter ? '' : 'card__footer--empty'}" part="footer">
|
|
91
|
+
<slot name="footer" @slotchange=${this._onFooterSlotChange}></slot>
|
|
92
|
+
</div>
|
|
93
|
+
`;
|
|
94
|
+
|
|
68
95
|
if (this.href) {
|
|
69
|
-
return html`<a class="card" href=${this.href} part="card"><div class="card__inner" part="inner"
|
|
96
|
+
return html`<a class="card" href=${this.href} part="card"><div class="card__inner" part="inner">${content}</div></a>`;
|
|
70
97
|
}
|
|
71
|
-
return html`<div class="card" part="card"><div class="card__inner" part="inner"
|
|
98
|
+
return html`<div class="card" part="card"><div class="card__inner" part="inner">${content}</div></div>`;
|
|
72
99
|
}
|
|
73
100
|
}
|
|
74
101
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { LitElement, html, css } from 'lit';
|
|
2
|
+
import { tokenStyles } from '../shared-styles.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @arc-prism content — call-to-action banner with gradient background
|
|
6
|
+
*/
|
|
7
|
+
export class ArcCtaBanner extends LitElement {
|
|
8
|
+
static properties = {
|
|
9
|
+
eyebrow: { type: String },
|
|
10
|
+
headline: { type: String },
|
|
11
|
+
nogradient: { type: Boolean, reflect: true },
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
static styles = [
|
|
15
|
+
tokenStyles,
|
|
16
|
+
css`
|
|
17
|
+
:host { display: block; position: relative; overflow: hidden; }
|
|
18
|
+
|
|
19
|
+
.cta {
|
|
20
|
+
position: relative;
|
|
21
|
+
padding: var(--space-3xl) var(--space-lg);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.cta__bg {
|
|
25
|
+
position: absolute;
|
|
26
|
+
inset: 0;
|
|
27
|
+
background:
|
|
28
|
+
radial-gradient(ellipse at 30% 50%, rgba(var(--accent-primary-rgb), 0.1), transparent 60%),
|
|
29
|
+
radial-gradient(ellipse at 70% 50%, rgba(var(--accent-secondary-rgb), 0.08), transparent 60%);
|
|
30
|
+
pointer-events: none;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
:host([nogradient]) .cta__bg { display: none; }
|
|
34
|
+
|
|
35
|
+
.cta__inner {
|
|
36
|
+
position: relative;
|
|
37
|
+
max-width: var(--max-width, 1200px);
|
|
38
|
+
margin-inline: auto;
|
|
39
|
+
display: flex;
|
|
40
|
+
flex-direction: column;
|
|
41
|
+
align-items: center;
|
|
42
|
+
text-align: center;
|
|
43
|
+
gap: var(--space-md);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.cta__eyebrow {
|
|
47
|
+
font-family: var(--font-accent);
|
|
48
|
+
font-weight: 600;
|
|
49
|
+
font-size: var(--text-xs);
|
|
50
|
+
letter-spacing: 4px;
|
|
51
|
+
text-transform: uppercase;
|
|
52
|
+
background: var(--gradient-accent-text);
|
|
53
|
+
-webkit-background-clip: text;
|
|
54
|
+
-webkit-text-fill-color: transparent;
|
|
55
|
+
background-clip: text;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.cta__headline {
|
|
59
|
+
font-size: clamp(28px, 4vw, 40px);
|
|
60
|
+
font-weight: 500;
|
|
61
|
+
letter-spacing: -1px;
|
|
62
|
+
background: var(--gradient-display-text);
|
|
63
|
+
-webkit-background-clip: text;
|
|
64
|
+
-webkit-text-fill-color: transparent;
|
|
65
|
+
background-clip: text;
|
|
66
|
+
margin: 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.cta__body {
|
|
70
|
+
color: var(--text-secondary);
|
|
71
|
+
font-size: var(--text-md);
|
|
72
|
+
max-width: 480px;
|
|
73
|
+
text-wrap: balance;
|
|
74
|
+
line-height: 1.7;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.cta__body ::slotted(*) { margin: 0; }
|
|
78
|
+
|
|
79
|
+
.cta__actions {
|
|
80
|
+
display: flex;
|
|
81
|
+
gap: var(--space-md);
|
|
82
|
+
margin-top: var(--space-sm);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@media (max-width: 768px) {
|
|
86
|
+
.cta { padding: var(--space-xl) var(--space-md); }
|
|
87
|
+
.cta__actions { flex-direction: column; align-items: center; }
|
|
88
|
+
}
|
|
89
|
+
`,
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
constructor() {
|
|
93
|
+
super();
|
|
94
|
+
this.eyebrow = '';
|
|
95
|
+
this.headline = '';
|
|
96
|
+
this.nogradient = false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
render() {
|
|
100
|
+
return html`
|
|
101
|
+
<div class="cta" part="container">
|
|
102
|
+
<div class="cta__bg" part="background"></div>
|
|
103
|
+
<div class="cta__inner" part="inner">
|
|
104
|
+
${this.eyebrow ? html`
|
|
105
|
+
<span class="cta__eyebrow" part="eyebrow">
|
|
106
|
+
<slot name="eyebrow">${this.eyebrow}</slot>
|
|
107
|
+
</span>
|
|
108
|
+
` : html`<slot name="eyebrow"></slot>`}
|
|
109
|
+
${this.headline ? html`
|
|
110
|
+
<h2 class="cta__headline" part="headline">
|
|
111
|
+
<slot name="headline">${this.headline}</slot>
|
|
112
|
+
</h2>
|
|
113
|
+
` : html`<slot name="headline"></slot>`}
|
|
114
|
+
<div class="cta__body" part="body">
|
|
115
|
+
<slot></slot>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="cta__actions" part="actions">
|
|
118
|
+
<slot name="actions"></slot>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
`;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
customElements.define('arc-cta-banner', ArcCtaBanner);
|
package/src/content/index.js
CHANGED
|
@@ -15,6 +15,7 @@ export { ArcCodeBlock } from './code-block.js';
|
|
|
15
15
|
export { ArcCollapsible } from './collapsible.js';
|
|
16
16
|
export { ArcColorSwatch } from './color-swatch.js';
|
|
17
17
|
export { ArcColumn } from './column.js';
|
|
18
|
+
export { ArcCtaBanner } from './cta-banner.js';
|
|
18
19
|
export { ArcDataTable } from './data-table.js';
|
|
19
20
|
export { ArcDivider } from './divider.js';
|
|
20
21
|
export { ArcEmptyState } from './empty-state.js';
|
package/src/content/tag.js
CHANGED
|
@@ -6,6 +6,7 @@ export class ArcTag extends LitElement {
|
|
|
6
6
|
variant: { type: String, reflect: true },
|
|
7
7
|
removable: { type: Boolean, reflect: true },
|
|
8
8
|
disabled: { type: Boolean, reflect: true },
|
|
9
|
+
color: { type: String },
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
static styles = [
|
|
@@ -120,6 +121,7 @@ export class ArcTag extends LitElement {
|
|
|
120
121
|
this.variant = 'default';
|
|
121
122
|
this.removable = false;
|
|
122
123
|
this.disabled = false;
|
|
124
|
+
this.color = '';
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
_remove(e) {
|
|
@@ -132,8 +134,15 @@ export class ArcTag extends LitElement {
|
|
|
132
134
|
}
|
|
133
135
|
|
|
134
136
|
render() {
|
|
137
|
+
const colorStyle = this.color
|
|
138
|
+
? `border-color: rgba(${this.color}, 0.2); color: rgb(${this.color}); background: rgba(${this.color}, 0.06);`
|
|
139
|
+
: '';
|
|
140
|
+
|
|
135
141
|
return html`
|
|
136
|
-
<span class="tag" part="tag"
|
|
142
|
+
<span class="tag" part="tag" style=${colorStyle}
|
|
143
|
+
@mouseenter=${this.color ? (e) => { e.currentTarget.style.boxShadow = `0 0 12px rgba(${this.color}, 0.15)`; } : null}
|
|
144
|
+
@mouseleave=${this.color ? (e) => { e.currentTarget.style.boxShadow = ''; } : null}
|
|
145
|
+
>
|
|
137
146
|
<span class="tag__label" part="label"><slot></slot></span>
|
|
138
147
|
${this.removable ? html`
|
|
139
148
|
<button
|
package/src/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ARC UI — Web Components
|
|
2
2
|
// Re-exports from all tiers
|
|
3
3
|
|
|
4
|
-
export { ArcAccordion, ArcAccordionItem, ArcAnimatedNumber, ArcAspectRatio, ArcAvatar, ArcAvatarGroup, ArcBadge, ArcCallout, ArcCard, ArcCarousel, ArcCodeBlock, ArcCollapsible, ArcColorSwatch, ArcColumn, ArcDataTable, ArcDivider, ArcEmptyState, ArcFeatureCard, ArcHighlight, ArcIcon, ArcIconLibrary, iconRegistry, ArcInfiniteScroll, ArcKbd, ArcMarkdown, ArcMarquee, ArcMeter, ArcScrollArea, ArcSkeleton, ArcSpinner, ArcStack, ArcStat, ArcStep, ArcStepper, ArcTable, ArcTag, ArcText, ArcTimeline, ArcTimelineItem, ArcTruncate, ArcValueCard } from './content/index.js';
|
|
4
|
+
export { ArcAccordion, ArcAccordionItem, ArcAnimatedNumber, ArcAspectRatio, ArcAvatar, ArcAvatarGroup, ArcBadge, ArcCallout, ArcCard, ArcCarousel, ArcCodeBlock, ArcCollapsible, ArcColorSwatch, ArcColumn, ArcDataTable, ArcDivider, ArcEmptyState, ArcFeatureCard, ArcHighlight, ArcIcon, ArcIconLibrary, iconRegistry, ArcInfiniteScroll, ArcKbd, ArcMarkdown, ArcMarquee, ArcMeter, ArcScrollArea, ArcSkeleton, ArcSpinner, ArcStack, ArcStat, ArcStep, ArcStepper, ArcTable, ArcTag, ArcText, ArcTimeline, ArcTimelineItem, ArcTruncate, ArcValueCard , ArcCtaBanner } from './content/index.js';
|
|
5
5
|
export { ArcButton, ArcCalendar, ArcCheckbox, ArcChip, ArcColorPicker, ArcCombobox, ArcCopyButton, ArcDatePicker, ArcFileUpload, ArcForm, ArcIconButton, ArcInput, ArcMultiSelect, ArcNumberInput, ArcOtpInput, ArcPinInput, ArcRadio, ArcRadioGroup, ArcRating, ArcSearch, ArcSegmentedControl, ArcSelect, ArcSlider, ArcSortableList, ArcSuggestion, ArcTextarea, ArcThemeToggle, ArcToggle } from './input/index.js';
|
|
6
6
|
export { ArcAlert, ArcCommandItem, ArcCommandPalette, ArcContextMenu, ArcDialog, ArcDropdownMenu, ArcHoverCard, ArcModal, ArcNotificationPanel, ArcPopover, ArcProgress, ArcSheet, ArcToast, ArcTooltip } from './feedback/index.js';
|
|
7
7
|
export { ArcBreadcrumb, ArcBreadcrumbItem, ArcDrawer, ArcFooter, ArcLink, ArcNavItem, ArcNavigationMenu, ArcPagination, ArcScrollSpy, ArcScrollToTop, ArcSidebar, ArcSidebarLink, ArcSidebarSection, ArcSpyLink, ArcTab, ArcTabs, ArcTopBar, ArcTreeItem, ArcTreeView } from './navigation/index.js';
|
package/src/input/button.js
CHANGED
|
@@ -3,11 +3,13 @@ import { tokenStyles } from '../shared-styles.js';
|
|
|
3
3
|
|
|
4
4
|
export class ArcButton extends LitElement {
|
|
5
5
|
static properties = {
|
|
6
|
-
variant:
|
|
7
|
-
size:
|
|
8
|
-
href:
|
|
9
|
-
disabled:
|
|
10
|
-
type:
|
|
6
|
+
variant: { type: String, reflect: true },
|
|
7
|
+
size: { type: String, reflect: true },
|
|
8
|
+
href: { type: String },
|
|
9
|
+
disabled: { type: Boolean, reflect: true },
|
|
10
|
+
type: { type: String },
|
|
11
|
+
_hasPrefix: { state: true },
|
|
12
|
+
_hasSuffix: { state: true },
|
|
11
13
|
};
|
|
12
14
|
|
|
13
15
|
static styles = [
|
|
@@ -95,6 +97,21 @@ export class ArcButton extends LitElement {
|
|
|
95
97
|
/* Disabled */
|
|
96
98
|
:host([disabled]) .btn { opacity: 0.4; cursor: not-allowed; pointer-events: none; }
|
|
97
99
|
|
|
100
|
+
/* Prefix / Suffix */
|
|
101
|
+
.btn__prefix,
|
|
102
|
+
.btn__suffix {
|
|
103
|
+
display: inline-flex;
|
|
104
|
+
align-items: center;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.btn__prefix--empty,
|
|
108
|
+
.btn__suffix--empty { display: none; }
|
|
109
|
+
|
|
110
|
+
::slotted([slot="prefix"]),
|
|
111
|
+
::slotted([slot="suffix"]) {
|
|
112
|
+
display: flex;
|
|
113
|
+
}
|
|
114
|
+
|
|
98
115
|
@media (prefers-reduced-motion: reduce) {
|
|
99
116
|
:host *,
|
|
100
117
|
:host *::before,
|
|
@@ -114,13 +131,35 @@ export class ArcButton extends LitElement {
|
|
|
114
131
|
this.href = '';
|
|
115
132
|
this.disabled = false;
|
|
116
133
|
this.type = 'button';
|
|
134
|
+
this._hasPrefix = false;
|
|
135
|
+
this._hasSuffix = false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
_onPrefixSlotChange(e) {
|
|
139
|
+
this._hasPrefix = e.target.assignedNodes({ flatten: true }).length > 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
_onSuffixSlotChange(e) {
|
|
143
|
+
this._hasSuffix = e.target.assignedNodes({ flatten: true }).length > 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
_renderContent() {
|
|
147
|
+
return html`
|
|
148
|
+
<span class="btn__prefix ${this._hasPrefix ? '' : 'btn__prefix--empty'}">
|
|
149
|
+
<slot name="prefix" @slotchange=${this._onPrefixSlotChange}></slot>
|
|
150
|
+
</span>
|
|
151
|
+
<slot></slot>
|
|
152
|
+
<span class="btn__suffix ${this._hasSuffix ? '' : 'btn__suffix--empty'}">
|
|
153
|
+
<slot name="suffix" @slotchange=${this._onSuffixSlotChange}></slot>
|
|
154
|
+
</span>
|
|
155
|
+
`;
|
|
117
156
|
}
|
|
118
157
|
|
|
119
158
|
render() {
|
|
120
159
|
if (this.href) {
|
|
121
|
-
return html`<a class="btn" href=${this.href} part="button"
|
|
160
|
+
return html`<a class="btn" href=${this.href} part="button">${this._renderContent()}</a>`;
|
|
122
161
|
}
|
|
123
|
-
return html`<button class="btn" type=${this.type} ?disabled=${this.disabled} part="button"
|
|
162
|
+
return html`<button class="btn" type=${this.type} ?disabled=${this.disabled} part="button">${this._renderContent()}</button>`;
|
|
124
163
|
}
|
|
125
164
|
}
|
|
126
165
|
|
package/src/input/form.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import { LitElement, html, css } from 'lit';
|
|
1
|
+
import { LitElement, html, css, nothing } from 'lit';
|
|
2
2
|
import { tokenStyles } from '../shared-styles.js';
|
|
3
3
|
|
|
4
4
|
export class ArcForm extends LitElement {
|
|
5
5
|
static properties = {
|
|
6
|
-
action:
|
|
7
|
-
method:
|
|
8
|
-
novalidate:
|
|
6
|
+
action: { type: String },
|
|
7
|
+
method: { type: String },
|
|
8
|
+
novalidate: { type: Boolean },
|
|
9
|
+
loading: { type: Boolean, reflect: true },
|
|
10
|
+
disabled: { type: Boolean, reflect: true },
|
|
11
|
+
errorSummary: { type: Boolean, attribute: 'error-summary' },
|
|
12
|
+
_errors: { state: true },
|
|
9
13
|
};
|
|
10
14
|
|
|
11
15
|
static styles = [
|
|
@@ -26,6 +30,45 @@ export class ArcForm extends LitElement {
|
|
|
26
30
|
::slotted(*) {
|
|
27
31
|
margin: 0;
|
|
28
32
|
}
|
|
33
|
+
|
|
34
|
+
/* Loading state */
|
|
35
|
+
:host([loading]) .form-layout {
|
|
36
|
+
pointer-events: none;
|
|
37
|
+
opacity: 0.7;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Disabled state */
|
|
41
|
+
:host([disabled]) .form-layout {
|
|
42
|
+
pointer-events: none;
|
|
43
|
+
opacity: 0.5;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Error summary */
|
|
47
|
+
.form-errors {
|
|
48
|
+
border: 1px solid var(--color-error, #ef4444);
|
|
49
|
+
border-radius: var(--radius-md);
|
|
50
|
+
background: rgba(239, 68, 68, 0.06);
|
|
51
|
+
padding: var(--space-sm) var(--space-md);
|
|
52
|
+
margin-bottom: var(--space-sm);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.form-errors__title {
|
|
56
|
+
font-family: var(--font-accent);
|
|
57
|
+
font-size: var(--text-xs);
|
|
58
|
+
font-weight: 600;
|
|
59
|
+
letter-spacing: 1px;
|
|
60
|
+
text-transform: uppercase;
|
|
61
|
+
color: var(--color-error, #ef4444);
|
|
62
|
+
margin: 0 0 var(--space-xs) 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.form-errors__list {
|
|
66
|
+
margin: 0;
|
|
67
|
+
padding: 0 0 0 var(--space-md);
|
|
68
|
+
font-size: var(--text-sm);
|
|
69
|
+
color: var(--color-error, #ef4444);
|
|
70
|
+
line-height: 1.6;
|
|
71
|
+
}
|
|
29
72
|
`,
|
|
30
73
|
];
|
|
31
74
|
|
|
@@ -34,15 +77,18 @@ export class ArcForm extends LitElement {
|
|
|
34
77
|
this.action = '';
|
|
35
78
|
this.method = '';
|
|
36
79
|
this.novalidate = false;
|
|
80
|
+
this.loading = false;
|
|
81
|
+
this.disabled = false;
|
|
82
|
+
this.errorSummary = true;
|
|
83
|
+
this._errors = [];
|
|
37
84
|
}
|
|
38
85
|
|
|
39
|
-
|
|
86
|
+
/** Gather all form controls and propagate disabled */
|
|
87
|
+
_getFormControls() {
|
|
40
88
|
const slot = this.shadowRoot.querySelector('slot');
|
|
41
89
|
const children = slot ? slot.assignedElements({ flatten: true }) : [];
|
|
42
|
-
const
|
|
43
|
-
const errors = [];
|
|
90
|
+
const controls = [];
|
|
44
91
|
|
|
45
|
-
const formControls = [];
|
|
46
92
|
const gather = (elements) => {
|
|
47
93
|
for (const el of elements) {
|
|
48
94
|
const tag = el.tagName?.toLowerCase();
|
|
@@ -54,7 +100,7 @@ export class ArcForm extends LitElement {
|
|
|
54
100
|
tag === 'arc-toggle' ||
|
|
55
101
|
tag === 'arc-radio-group'
|
|
56
102
|
) {
|
|
57
|
-
|
|
103
|
+
controls.push(el);
|
|
58
104
|
}
|
|
59
105
|
if (!el.shadowRoot && el.children?.length) {
|
|
60
106
|
gather([...el.children]);
|
|
@@ -63,15 +109,42 @@ export class ArcForm extends LitElement {
|
|
|
63
109
|
};
|
|
64
110
|
|
|
65
111
|
gather(children);
|
|
112
|
+
return controls;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
updated(changed) {
|
|
116
|
+
super.updated(changed);
|
|
117
|
+
|
|
118
|
+
if (changed.has('disabled')) {
|
|
119
|
+
const controls = this._getFormControls();
|
|
120
|
+
for (const control of controls) {
|
|
121
|
+
if (this.disabled) {
|
|
122
|
+
control.setAttribute('disabled', '');
|
|
123
|
+
} else {
|
|
124
|
+
control.removeAttribute('disabled');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
_collectValues() {
|
|
131
|
+
const controls = this._getFormControls();
|
|
132
|
+
const values = {};
|
|
133
|
+
const formData = new FormData();
|
|
134
|
+
const errors = [];
|
|
66
135
|
|
|
67
|
-
for (const control of
|
|
136
|
+
for (const control of controls) {
|
|
68
137
|
const name = control.getAttribute('name') || control.label || control.tagName.toLowerCase();
|
|
69
138
|
const tag = control.tagName.toLowerCase();
|
|
70
139
|
|
|
71
140
|
if (tag === 'arc-checkbox' || tag === 'arc-toggle') {
|
|
72
141
|
values[name] = control.checked ?? false;
|
|
142
|
+
if (control.checked) {
|
|
143
|
+
formData.append(name, 'on');
|
|
144
|
+
}
|
|
73
145
|
} else {
|
|
74
146
|
values[name] = control.value ?? '';
|
|
147
|
+
formData.append(name, control.value ?? '');
|
|
75
148
|
}
|
|
76
149
|
|
|
77
150
|
const required = control.hasAttribute('required');
|
|
@@ -93,14 +166,20 @@ export class ArcForm extends LitElement {
|
|
|
93
166
|
}
|
|
94
167
|
}
|
|
95
168
|
|
|
96
|
-
return { values, errors, valid: errors.length === 0 };
|
|
169
|
+
return { values, formData, errors, valid: errors.length === 0 };
|
|
97
170
|
}
|
|
98
171
|
|
|
99
172
|
_handleSubmit(e) {
|
|
100
|
-
|
|
173
|
+
if (this.loading) {
|
|
174
|
+
e.preventDefault();
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const { values, formData, errors, valid } = this._collectValues();
|
|
101
179
|
|
|
102
180
|
if (!valid && !this.novalidate) {
|
|
103
181
|
e.preventDefault();
|
|
182
|
+
this._errors = errors;
|
|
104
183
|
this.dispatchEvent(new CustomEvent('arc-invalid', {
|
|
105
184
|
detail: { errors },
|
|
106
185
|
bubbles: true,
|
|
@@ -109,10 +188,12 @@ export class ArcForm extends LitElement {
|
|
|
109
188
|
return;
|
|
110
189
|
}
|
|
111
190
|
|
|
191
|
+
this._errors = [];
|
|
192
|
+
|
|
112
193
|
// Native form submission — let the browser handle it
|
|
113
194
|
if (this.action) {
|
|
114
195
|
this.dispatchEvent(new CustomEvent('arc-submit', {
|
|
115
|
-
detail: { values, valid },
|
|
196
|
+
detail: { values, formData, valid },
|
|
116
197
|
bubbles: true,
|
|
117
198
|
composed: true,
|
|
118
199
|
}));
|
|
@@ -123,7 +204,7 @@ export class ArcForm extends LitElement {
|
|
|
123
204
|
// JS-only mode — prevent default and let the listener handle it
|
|
124
205
|
e.preventDefault();
|
|
125
206
|
this.dispatchEvent(new CustomEvent('arc-submit', {
|
|
126
|
-
detail: { values, valid },
|
|
207
|
+
detail: { values, formData, valid },
|
|
127
208
|
bubbles: true,
|
|
128
209
|
composed: true,
|
|
129
210
|
}));
|
|
@@ -136,27 +217,26 @@ export class ArcForm extends LitElement {
|
|
|
136
217
|
|
|
137
218
|
/** Reset error states on child controls */
|
|
138
219
|
reset() {
|
|
139
|
-
const
|
|
140
|
-
const children = slot ? slot.assignedElements({ flatten: true }) : [];
|
|
220
|
+
const controls = this._getFormControls();
|
|
141
221
|
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
el.error = '';
|
|
146
|
-
}
|
|
147
|
-
if (typeof el.value === 'string') {
|
|
148
|
-
el.value = '';
|
|
149
|
-
}
|
|
150
|
-
if (typeof el.checked === 'boolean') {
|
|
151
|
-
el.checked = false;
|
|
152
|
-
}
|
|
153
|
-
if (!el.shadowRoot && el.children?.length) {
|
|
154
|
-
clearErrors([...el.children]);
|
|
155
|
-
}
|
|
222
|
+
for (const control of controls) {
|
|
223
|
+
if (typeof control.error !== 'undefined') {
|
|
224
|
+
control.error = '';
|
|
156
225
|
}
|
|
157
|
-
|
|
226
|
+
if (typeof control.value === 'string') {
|
|
227
|
+
control.value = '';
|
|
228
|
+
}
|
|
229
|
+
if (typeof control.checked === 'boolean') {
|
|
230
|
+
control.checked = false;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
this._errors = [];
|
|
158
235
|
|
|
159
|
-
|
|
236
|
+
this.dispatchEvent(new CustomEvent('arc-reset', {
|
|
237
|
+
bubbles: true,
|
|
238
|
+
composed: true,
|
|
239
|
+
}));
|
|
160
240
|
}
|
|
161
241
|
|
|
162
242
|
render() {
|
|
@@ -169,6 +249,14 @@ export class ArcForm extends LitElement {
|
|
|
169
249
|
@submit=${this._handleSubmit}
|
|
170
250
|
>
|
|
171
251
|
<div class="form-layout" part="layout">
|
|
252
|
+
${this.errorSummary && this._errors.length > 0 ? html`
|
|
253
|
+
<div class="form-errors" role="alert" part="errors">
|
|
254
|
+
<p class="form-errors__title">Please fix the following errors</p>
|
|
255
|
+
<ul class="form-errors__list">
|
|
256
|
+
${this._errors.map(err => html`<li>${err.message}</li>`)}
|
|
257
|
+
</ul>
|
|
258
|
+
</div>
|
|
259
|
+
` : nothing}
|
|
172
260
|
<slot></slot>
|
|
173
261
|
</div>
|
|
174
262
|
</form>
|
package/src/input/input.js
CHANGED
|
@@ -16,6 +16,8 @@ export class ArcInput extends LitElement {
|
|
|
16
16
|
required: { type: Boolean },
|
|
17
17
|
multiline: { type: Boolean },
|
|
18
18
|
rows: { type: Number },
|
|
19
|
+
_hasPrefix: { state: true },
|
|
20
|
+
_hasSuffix: { state: true },
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
static styles = [
|
|
@@ -38,15 +40,12 @@ export class ArcInput extends LitElement {
|
|
|
38
40
|
color: var(--text-muted);
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
.input-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
font-weight: 300;
|
|
45
|
-
color: var(--text-primary);
|
|
43
|
+
.input-group__wrapper {
|
|
44
|
+
display: flex;
|
|
45
|
+
align-items: center;
|
|
46
46
|
background: var(--bg-surface);
|
|
47
47
|
border: 1px solid var(--border-default);
|
|
48
48
|
border-radius: var(--radius-md);
|
|
49
|
-
padding: var(--space-sm) var(--space-md);
|
|
50
49
|
transition:
|
|
51
50
|
border-color var(--transition-fast),
|
|
52
51
|
box-shadow var(--transition-fast),
|
|
@@ -55,17 +54,53 @@ export class ArcInput extends LitElement {
|
|
|
55
54
|
width: 100%;
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
.input-group__field::placeholder { color: var(--text-ghost); }
|
|
61
|
-
.input-group__field:hover:not(:focus) { border-color: var(--border-bright); }
|
|
62
|
-
.input-group__field:focus {
|
|
63
|
-
outline: none;
|
|
57
|
+
.input-group__wrapper:hover:not(:focus-within) { border-color: var(--border-bright); }
|
|
58
|
+
.input-group__wrapper:focus-within {
|
|
64
59
|
border-color: rgba(var(--accent-primary-rgb), 0.4);
|
|
65
60
|
box-shadow: var(--focus-glow);
|
|
66
61
|
background: var(--bg-card);
|
|
67
62
|
}
|
|
68
|
-
|
|
63
|
+
|
|
64
|
+
:host([disabled]) .input-group__wrapper { opacity: 0.4; cursor: not-allowed; }
|
|
65
|
+
|
|
66
|
+
.input-group__field {
|
|
67
|
+
font-family: var(--font-body);
|
|
68
|
+
font-size: var(--body-size);
|
|
69
|
+
font-weight: 300;
|
|
70
|
+
color: var(--text-primary);
|
|
71
|
+
background: transparent;
|
|
72
|
+
border: none;
|
|
73
|
+
padding: var(--space-sm) var(--space-md);
|
|
74
|
+
box-sizing: border-box;
|
|
75
|
+
width: 100%;
|
|
76
|
+
min-width: 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.input-group__field:focus { outline: none; }
|
|
80
|
+
.input-group__field::placeholder { color: var(--text-ghost); }
|
|
81
|
+
.input-group__field:disabled { cursor: not-allowed; }
|
|
82
|
+
|
|
83
|
+
textarea.input-group__field { resize: vertical; }
|
|
84
|
+
|
|
85
|
+
.input-group__prefix,
|
|
86
|
+
.input-group__suffix {
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: center;
|
|
89
|
+
color: var(--text-muted);
|
|
90
|
+
flex-shrink: 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.input-group__prefix { padding-left: var(--space-md); }
|
|
94
|
+
.input-group__suffix { padding-right: var(--space-md); }
|
|
95
|
+
|
|
96
|
+
.input-group__prefix--empty,
|
|
97
|
+
.input-group__suffix--empty { display: none; }
|
|
98
|
+
|
|
99
|
+
::slotted([slot="prefix"]),
|
|
100
|
+
::slotted([slot="suffix"]) {
|
|
101
|
+
width: 20px;
|
|
102
|
+
height: 20px;
|
|
103
|
+
}
|
|
69
104
|
|
|
70
105
|
@media (prefers-reduced-motion: reduce) {
|
|
71
106
|
:host *,
|
|
@@ -92,6 +127,8 @@ export class ArcInput extends LitElement {
|
|
|
92
127
|
this.multiline = false;
|
|
93
128
|
this.rows = 5;
|
|
94
129
|
this._fieldId = `arc-input-${++inputIdCounter}`;
|
|
130
|
+
this._hasPrefix = false;
|
|
131
|
+
this._hasSuffix = false;
|
|
95
132
|
}
|
|
96
133
|
|
|
97
134
|
updated(changed) {
|
|
@@ -112,6 +149,14 @@ export class ArcInput extends LitElement {
|
|
|
112
149
|
this.dispatchEvent(new CustomEvent('arc-change', { detail: { value: this.value }, bubbles: true, composed: true }));
|
|
113
150
|
}
|
|
114
151
|
|
|
152
|
+
_onPrefixSlotChange(e) {
|
|
153
|
+
this._hasPrefix = e.target.assignedNodes({ flatten: true }).length > 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
_onSuffixSlotChange(e) {
|
|
157
|
+
this._hasSuffix = e.target.assignedNodes({ flatten: true }).length > 0;
|
|
158
|
+
}
|
|
159
|
+
|
|
115
160
|
render() {
|
|
116
161
|
const id = this.name || this._fieldId;
|
|
117
162
|
const field = this.multiline
|
|
@@ -149,7 +194,15 @@ export class ArcInput extends LitElement {
|
|
|
149
194
|
return html`
|
|
150
195
|
<div class="input-group">
|
|
151
196
|
${this.label ? html`<label class="input-group__label" for=${id} part="label">${this.label}</label>` : ''}
|
|
152
|
-
|
|
197
|
+
<div class="input-group__wrapper" part="wrapper">
|
|
198
|
+
<div class="input-group__prefix ${this._hasPrefix ? '' : 'input-group__prefix--empty'}" part="prefix">
|
|
199
|
+
<slot name="prefix" @slotchange=${this._onPrefixSlotChange}></slot>
|
|
200
|
+
</div>
|
|
201
|
+
${field}
|
|
202
|
+
<div class="input-group__suffix ${this._hasSuffix ? '' : 'input-group__suffix--empty'}" part="suffix">
|
|
203
|
+
<slot name="suffix" @slotchange=${this._onSuffixSlotChange}></slot>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
153
206
|
</div>
|
|
154
207
|
`;
|
|
155
208
|
}
|
|
@@ -5,6 +5,7 @@ export class ArcPageHeader extends LitElement {
|
|
|
5
5
|
static properties = {
|
|
6
6
|
heading: { type: String },
|
|
7
7
|
description: { type: String },
|
|
8
|
+
border: { type: Boolean, reflect: true },
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
static styles = [
|
|
@@ -17,10 +18,13 @@ export class ArcPageHeader extends LitElement {
|
|
|
17
18
|
|
|
18
19
|
.page-header {
|
|
19
20
|
padding: var(--space-lg) 0 var(--space-md);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
:host([border]) .page-header {
|
|
20
24
|
border-bottom: 1px solid var(--border-subtle);
|
|
21
25
|
}
|
|
22
26
|
|
|
23
|
-
.page-
|
|
27
|
+
.page-header__above {
|
|
24
28
|
margin-bottom: var(--space-sm);
|
|
25
29
|
}
|
|
26
30
|
|
|
@@ -35,13 +39,13 @@ export class ArcPageHeader extends LitElement {
|
|
|
35
39
|
.page-header__heading {
|
|
36
40
|
margin: 0;
|
|
37
41
|
font-family: var(--font-body);
|
|
38
|
-
font-size: 28px;
|
|
42
|
+
font-size: 28px;
|
|
39
43
|
font-weight: 700;
|
|
40
44
|
color: var(--text-primary);
|
|
41
45
|
line-height: 1.2;
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
.page-
|
|
48
|
+
.page-header__aside {
|
|
45
49
|
display: flex;
|
|
46
50
|
align-items: center;
|
|
47
51
|
gap: var(--space-sm);
|
|
@@ -55,7 +59,7 @@ export class ArcPageHeader extends LitElement {
|
|
|
55
59
|
line-height: 1.5;
|
|
56
60
|
}
|
|
57
61
|
|
|
58
|
-
.page-
|
|
62
|
+
.page-header__below {
|
|
59
63
|
margin-top: var(--space-md);
|
|
60
64
|
}
|
|
61
65
|
|
|
@@ -69,25 +73,26 @@ export class ArcPageHeader extends LitElement {
|
|
|
69
73
|
super();
|
|
70
74
|
this.heading = '';
|
|
71
75
|
this.description = '';
|
|
76
|
+
this.border = false;
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
render() {
|
|
75
80
|
return html`
|
|
76
81
|
<div class="page-header" part="base">
|
|
77
|
-
<div class="page-
|
|
78
|
-
<slot name="
|
|
82
|
+
<div class="page-header__above" part="above">
|
|
83
|
+
<slot name="above"></slot>
|
|
79
84
|
</div>
|
|
80
85
|
<div class="page-header__title-row" part="title-row">
|
|
81
86
|
<h1 class="page-header__heading" part="heading">${this.heading}</h1>
|
|
82
|
-
<div class="page-
|
|
83
|
-
<slot name="
|
|
87
|
+
<div class="page-header__aside" part="aside">
|
|
88
|
+
<slot name="aside"></slot>
|
|
84
89
|
</div>
|
|
85
90
|
</div>
|
|
86
91
|
${this.description
|
|
87
92
|
? html`<p class="page-header__description" part="description">${this.description}</p>`
|
|
88
93
|
: ''}
|
|
89
|
-
<div class="page-
|
|
90
|
-
<slot name="
|
|
94
|
+
<div class="page-header__below" part="below">
|
|
95
|
+
<slot name="below"></slot>
|
|
91
96
|
</div>
|
|
92
97
|
<div class="page-header__content" part="content">
|
|
93
98
|
<slot></slot>
|
|
@@ -7,6 +7,7 @@ export class ArcNavItem extends LitElement {
|
|
|
7
7
|
static properties = {
|
|
8
8
|
href: { type: String, reflect: true },
|
|
9
9
|
active: { type: Boolean, reflect: true },
|
|
10
|
+
muted: { type: Boolean, reflect: true },
|
|
10
11
|
description: { type: String },
|
|
11
12
|
};
|
|
12
13
|
|
|
@@ -18,6 +19,7 @@ export class ArcNavItem extends LitElement {
|
|
|
18
19
|
super();
|
|
19
20
|
this.href = '';
|
|
20
21
|
this.active = false;
|
|
22
|
+
this.muted = false;
|
|
21
23
|
this.description = '';
|
|
22
24
|
}
|
|
23
25
|
|
|
@@ -71,6 +71,18 @@ export class ArcNavigationMenu extends LitElement {
|
|
|
71
71
|
box-shadow: inset 0 0 8px rgba(var(--accent-primary-rgb), 0.08), 0 0 12px rgba(var(--accent-primary-rgb), 0.12);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
.nav__trigger--muted {
|
|
75
|
+
color: var(--text-muted);
|
|
76
|
+
font-weight: 500;
|
|
77
|
+
border-color: transparent;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.nav__trigger--muted:hover {
|
|
81
|
+
color: var(--text-secondary);
|
|
82
|
+
background: transparent;
|
|
83
|
+
border-color: transparent;
|
|
84
|
+
}
|
|
85
|
+
|
|
74
86
|
.nav__trigger:focus-visible {
|
|
75
87
|
outline: none;
|
|
76
88
|
box-shadow: var(--focus-glow);
|
|
@@ -112,7 +124,6 @@ export class ArcNavigationMenu extends LitElement {
|
|
|
112
124
|
|
|
113
125
|
.nav__dropdown-item {
|
|
114
126
|
display: block;
|
|
115
|
-
width: 100%;
|
|
116
127
|
text-align: left;
|
|
117
128
|
background: none;
|
|
118
129
|
border: none;
|
|
@@ -294,6 +305,15 @@ export class ArcNavigationMenu extends LitElement {
|
|
|
294
305
|
background: rgba(var(--accent-primary-rgb), 0.1);
|
|
295
306
|
}
|
|
296
307
|
|
|
308
|
+
.mobile-trigger--muted {
|
|
309
|
+
color: var(--text-muted);
|
|
310
|
+
font-weight: 400;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.mobile-trigger--muted:hover {
|
|
314
|
+
color: var(--text-secondary);
|
|
315
|
+
}
|
|
316
|
+
|
|
297
317
|
.mobile-chevron {
|
|
298
318
|
width: 12px;
|
|
299
319
|
height: 12px;
|
|
@@ -569,7 +589,7 @@ export class ArcNavigationMenu extends LitElement {
|
|
|
569
589
|
>
|
|
570
590
|
${hasChildren ? html`
|
|
571
591
|
<button
|
|
572
|
-
class="nav__trigger nav__trigger--has-children ${isOpen ? 'nav__trigger--open' : ''} ${item.active ? 'nav__trigger--active' : ''}"
|
|
592
|
+
class="nav__trigger nav__trigger--has-children ${isOpen ? 'nav__trigger--open' : ''} ${item.active ? 'nav__trigger--active' : ''} ${item.muted ? 'nav__trigger--muted' : ''}"
|
|
573
593
|
@click=${(e) => this._handleTriggerClick(e, item, i)}
|
|
574
594
|
aria-expanded=${String(isOpen)}
|
|
575
595
|
aria-haspopup="true"
|
|
@@ -582,7 +602,7 @@ export class ArcNavigationMenu extends LitElement {
|
|
|
582
602
|
</button>
|
|
583
603
|
` : html`
|
|
584
604
|
<a
|
|
585
|
-
class="nav__trigger ${item.active ? 'nav__trigger--active' : ''}"
|
|
605
|
+
class="nav__trigger ${item.active ? 'nav__trigger--active' : ''} ${item.muted ? 'nav__trigger--muted' : ''}"
|
|
586
606
|
href=${item.href}
|
|
587
607
|
@click=${(e) => this._handleTriggerClick(e, item, i)}
|
|
588
608
|
part="trigger"
|
|
@@ -643,7 +663,7 @@ export class ArcNavigationMenu extends LitElement {
|
|
|
643
663
|
<li class="mobile-item" style="animation-delay: ${i * 60}ms">
|
|
644
664
|
${hasChildren ? html`
|
|
645
665
|
<button
|
|
646
|
-
class="mobile-trigger ${item.active ? 'mobile-trigger--active' : ''}"
|
|
666
|
+
class="mobile-trigger ${item.active ? 'mobile-trigger--active' : ''} ${item.muted ? 'mobile-trigger--muted' : ''}"
|
|
647
667
|
@click=${() => this._toggleMobileDropdown(i)}
|
|
648
668
|
aria-expanded=${String(isExpanded)}
|
|
649
669
|
>
|
|
@@ -668,7 +688,7 @@ export class ArcNavigationMenu extends LitElement {
|
|
|
668
688
|
</div>
|
|
669
689
|
` : html`
|
|
670
690
|
<a
|
|
671
|
-
class="mobile-trigger ${item.active ? 'mobile-trigger--active' : ''}"
|
|
691
|
+
class="mobile-trigger ${item.active ? 'mobile-trigger--active' : ''} ${item.muted ? 'mobile-trigger--muted' : ''}"
|
|
672
692
|
href=${item.href}
|
|
673
693
|
@click=${(e) => this._handleMobileTriggerClick(e, item, i)}
|
|
674
694
|
>
|