@arclux/arc-ui 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.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +64 -0
  3. package/package.json +186 -0
  4. package/src/content/accordion-item.js +27 -0
  5. package/src/content/accordion.js +151 -0
  6. package/src/content/animated-number.js +160 -0
  7. package/src/content/aspect-ratio.js +78 -0
  8. package/src/content/avatar-group.js +101 -0
  9. package/src/content/avatar.js +92 -0
  10. package/src/content/badge.js +98 -0
  11. package/src/content/callout.js +141 -0
  12. package/src/content/card.js +75 -0
  13. package/src/content/carousel.js +300 -0
  14. package/src/content/code-block.js +152 -0
  15. package/src/content/collapsible.js +142 -0
  16. package/src/content/color-swatch.js +86 -0
  17. package/src/content/column.js +28 -0
  18. package/src/content/data-table.js +332 -0
  19. package/src/content/divider.js +153 -0
  20. package/src/content/empty-state.js +78 -0
  21. package/src/content/feature-card.js +142 -0
  22. package/src/content/highlight.js +63 -0
  23. package/src/content/icon-library.js +30 -0
  24. package/src/content/icon-registry.js +39 -0
  25. package/src/content/icon.js +95 -0
  26. package/src/content/index.js +44 -0
  27. package/src/content/infinite-scroll.js +144 -0
  28. package/src/content/kbd.js +40 -0
  29. package/src/content/markdown.js +294 -0
  30. package/src/content/marquee.js +166 -0
  31. package/src/content/meter.js +167 -0
  32. package/src/content/scroll-area.js +107 -0
  33. package/src/content/skeleton.js +85 -0
  34. package/src/content/spinner.js +77 -0
  35. package/src/content/stack.js +68 -0
  36. package/src/content/stat.js +72 -0
  37. package/src/content/step.js +22 -0
  38. package/src/content/stepper.js +202 -0
  39. package/src/content/table.js +134 -0
  40. package/src/content/tag.js +156 -0
  41. package/src/content/text.js +111 -0
  42. package/src/content/timeline-item.js +29 -0
  43. package/src/content/timeline.js +170 -0
  44. package/src/content/truncate.js +161 -0
  45. package/src/content/value-card.js +94 -0
  46. package/src/feedback/alert.js +187 -0
  47. package/src/feedback/command-item.js +28 -0
  48. package/src/feedback/command-palette.js +346 -0
  49. package/src/feedback/context-menu.js +299 -0
  50. package/src/feedback/dialog.js +298 -0
  51. package/src/feedback/dropdown-menu.js +259 -0
  52. package/src/feedback/hover-card.js +186 -0
  53. package/src/feedback/index.js +17 -0
  54. package/src/feedback/modal.js +226 -0
  55. package/src/feedback/notification-panel.js +196 -0
  56. package/src/feedback/popover.js +184 -0
  57. package/src/feedback/progress.js +169 -0
  58. package/src/feedback/sheet.js +249 -0
  59. package/src/feedback/toast.js +207 -0
  60. package/src/feedback/tooltip.js +189 -0
  61. package/src/icons/lucide.d.ts +1915 -0
  62. package/src/icons/lucide.js +1915 -0
  63. package/src/icons/phosphor.d.ts +1517 -0
  64. package/src/icons/phosphor.js +1517 -0
  65. package/src/icons/types.d.ts +8 -0
  66. package/src/index.js +9 -0
  67. package/src/input/button.js +127 -0
  68. package/src/input/calendar.js +340 -0
  69. package/src/input/checkbox.js +159 -0
  70. package/src/input/chip.js +120 -0
  71. package/src/input/color-picker.js +461 -0
  72. package/src/input/combobox.js +295 -0
  73. package/src/input/copy-button.js +144 -0
  74. package/src/input/date-picker.js +534 -0
  75. package/src/input/file-upload.js +333 -0
  76. package/src/input/form.js +179 -0
  77. package/src/input/icon-button.js +179 -0
  78. package/src/input/index.js +31 -0
  79. package/src/input/input.js +158 -0
  80. package/src/input/multi-select.js +392 -0
  81. package/src/input/number-input.js +239 -0
  82. package/src/input/otp-input.js +221 -0
  83. package/src/input/pin-input.js +294 -0
  84. package/src/input/radio-group.js +177 -0
  85. package/src/input/radio.js +28 -0
  86. package/src/input/rating.js +195 -0
  87. package/src/input/search.js +371 -0
  88. package/src/input/segmented-control.js +174 -0
  89. package/src/input/select.js +267 -0
  90. package/src/input/slider.js +217 -0
  91. package/src/input/sortable-list.js +345 -0
  92. package/src/input/suggestion.js +26 -0
  93. package/src/input/textarea.js +203 -0
  94. package/src/input/theme-toggle.js +196 -0
  95. package/src/input/toggle.js +166 -0
  96. package/src/layout/app-shell.js +266 -0
  97. package/src/layout/auth-shell.js +153 -0
  98. package/src/layout/container.js +37 -0
  99. package/src/layout/dashboard-grid.js +62 -0
  100. package/src/layout/index.js +15 -0
  101. package/src/layout/page-header.js +100 -0
  102. package/src/layout/page-layout.js +112 -0
  103. package/src/layout/resizable.js +221 -0
  104. package/src/layout/section.js +54 -0
  105. package/src/layout/settings-layout.js +91 -0
  106. package/src/layout/split-pane.js +172 -0
  107. package/src/layout/status-bar.js +84 -0
  108. package/src/layout/toolbar.js +92 -0
  109. package/src/navigation/breadcrumb-item.js +26 -0
  110. package/src/navigation/breadcrumb.js +129 -0
  111. package/src/navigation/drawer.js +183 -0
  112. package/src/navigation/footer.js +99 -0
  113. package/src/navigation/index.js +22 -0
  114. package/src/navigation/link.js +120 -0
  115. package/src/navigation/nav-item.js +46 -0
  116. package/src/navigation/navigation-menu.js +687 -0
  117. package/src/navigation/pagination.js +186 -0
  118. package/src/navigation/scroll-spy.js +198 -0
  119. package/src/navigation/scroll-to-top.js +163 -0
  120. package/src/navigation/sidebar-link.js +28 -0
  121. package/src/navigation/sidebar-section.js +45 -0
  122. package/src/navigation/sidebar.js +336 -0
  123. package/src/navigation/spy-link.js +26 -0
  124. package/src/navigation/tab.js +26 -0
  125. package/src/navigation/tabs.js +202 -0
  126. package/src/navigation/top-bar.js +263 -0
  127. package/src/navigation/tree-item.js +38 -0
  128. package/src/navigation/tree-view.js +255 -0
  129. package/src/shared/index.js +6 -0
  130. package/src/shared/menu-divider.js +9 -0
  131. package/src/shared/menu-item.js +30 -0
  132. package/src/shared/option.js +31 -0
  133. package/src/shared-styles.js +81 -0
  134. package/src/tokens.js +445 -0
  135. package/types/index.d.ts +973 -0
@@ -0,0 +1,295 @@
1
+ import { LitElement, html, css } from 'lit';
2
+ import { tokenStyles } from '../shared-styles.js';
3
+ import '../shared/option.js';
4
+
5
+ export class ArcCombobox extends LitElement {
6
+ static properties = {
7
+ value: { type: String, reflect: true },
8
+ placeholder: { type: String },
9
+ label: { type: String },
10
+ disabled: { type: Boolean, reflect: true },
11
+ _query: { state: true },
12
+ _open: { state: true },
13
+ _activeIndex: { state: true },
14
+ _options: { state: true },
15
+ };
16
+
17
+ static styles = [
18
+ tokenStyles,
19
+ css`
20
+ :host {
21
+ display: block;
22
+ position: relative;
23
+ font-family: var(--font-body);
24
+ }
25
+
26
+ :host([disabled]) {
27
+ opacity: 0.5;
28
+ pointer-events: none;
29
+ }
30
+
31
+ .combobox__label {
32
+ display: block;
33
+ font-size: var(--text-xs);
34
+ font-weight: 600;
35
+ color: var(--text-secondary);
36
+ margin-bottom: var(--space-xs);
37
+ letter-spacing: 0.5px;
38
+ }
39
+
40
+ .combobox__wrapper {
41
+ position: relative;
42
+ }
43
+
44
+ .combobox__input {
45
+ width: 100%;
46
+ box-sizing: border-box;
47
+ font-family: var(--font-body);
48
+ font-size: var(--text-sm);
49
+ color: var(--text-primary);
50
+ background: var(--bg-card);
51
+ border: 1px solid var(--border-default);
52
+ border-radius: var(--radius-md);
53
+ padding: var(--space-sm) var(--space-md);
54
+ outline: none;
55
+ transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
56
+ }
57
+
58
+ .combobox__input::placeholder {
59
+ color: var(--text-muted);
60
+ }
61
+
62
+ .combobox__input:focus {
63
+ outline: none;
64
+ border-color: rgba(var(--accent-primary-rgb), 0.4);
65
+ box-shadow: var(--focus-glow);
66
+ }
67
+
68
+ .combobox__listbox {
69
+ position: absolute;
70
+ top: calc(100% + 4px);
71
+ left: 0;
72
+ right: 0;
73
+ max-height: 220px;
74
+ overflow-y: auto;
75
+ background: var(--bg-card);
76
+ border: 1px solid var(--border-default);
77
+ border-radius: var(--radius-md);
78
+ box-shadow: var(--shadow-overlay);
79
+ z-index: 1000;
80
+ padding: var(--space-xs) 0;
81
+ display: none;
82
+ }
83
+
84
+ .combobox__listbox--open {
85
+ display: block;
86
+ }
87
+
88
+ .combobox__option {
89
+ display: block;
90
+ width: 100%;
91
+ text-align: left;
92
+ font-family: var(--font-body);
93
+ font-size: var(--text-sm);
94
+ color: var(--text-primary);
95
+ background: none;
96
+ border: none;
97
+ padding: var(--space-sm) var(--space-md);
98
+ cursor: pointer;
99
+ transition: background var(--transition-fast);
100
+ }
101
+
102
+ .combobox__option:hover,
103
+ .combobox__option--active {
104
+ background: rgba(var(--accent-primary-rgb), 0.1);
105
+ }
106
+
107
+ .combobox__option--selected {
108
+ color: var(--accent-primary);
109
+ }
110
+
111
+ .combobox__option:focus-visible {
112
+ outline: none;
113
+ box-shadow: var(--focus-glow);
114
+ }
115
+
116
+ .combobox__empty {
117
+ padding: var(--space-sm) var(--space-md);
118
+ font-size: var(--text-sm);
119
+ color: var(--text-muted);
120
+ }
121
+
122
+ .combobox__slot-host { display: none; }
123
+
124
+ @media (prefers-reduced-motion: reduce) {
125
+ :host *,
126
+ :host *::before,
127
+ :host *::after {
128
+ animation-duration: 0.01ms !important;
129
+ animation-iteration-count: 1 !important;
130
+ transition-duration: 0.01ms !important;
131
+ }
132
+ }
133
+ `,
134
+ ];
135
+
136
+ static _idCounter = 0;
137
+
138
+ constructor() {
139
+ super();
140
+ this.value = '';
141
+ this.placeholder = '';
142
+ this.label = '';
143
+ this.disabled = false;
144
+ this._query = '';
145
+ this._open = false;
146
+ this._activeIndex = -1;
147
+ this._options = [];
148
+ this._comboId = `combobox-${++ArcCombobox._idCounter}`;
149
+ this._onDocClick = this._onDocClick.bind(this);
150
+ }
151
+
152
+ connectedCallback() {
153
+ super.connectedCallback();
154
+ document.addEventListener('click', this._onDocClick, true);
155
+ }
156
+
157
+ disconnectedCallback() {
158
+ super.disconnectedCallback();
159
+ document.removeEventListener('click', this._onDocClick, true);
160
+ }
161
+
162
+ _onSlotChange(e) {
163
+ this._options = e.target.assignedElements({ flatten: true })
164
+ .filter(el => el.tagName === 'ARC-OPTION');
165
+ }
166
+
167
+ _onDocClick(e) {
168
+ if (!e.composedPath().includes(this)) {
169
+ this._open = false;
170
+ }
171
+ }
172
+
173
+ get _normalizedItems() {
174
+ return this._options.map(opt => ({ label: opt.label, value: opt.value }));
175
+ }
176
+
177
+ get _filteredItems() {
178
+ const q = this._query.toLowerCase();
179
+ if (!q) return this._normalizedItems;
180
+ return this._normalizedItems.filter(item =>
181
+ item.label.toLowerCase().includes(q)
182
+ );
183
+ }
184
+
185
+ _onInput(e) {
186
+ this._query = e.target.value;
187
+ this._open = true;
188
+ this._activeIndex = -1;
189
+ }
190
+
191
+ _onFocus() {
192
+ this._open = true;
193
+ }
194
+
195
+ _selectItem(item) {
196
+ this.value = item.value;
197
+ this._query = item.label;
198
+ this._open = false;
199
+ this._activeIndex = -1;
200
+
201
+ this.dispatchEvent(new CustomEvent('arc-change', {
202
+ detail: { value: item.value, item },
203
+ bubbles: true,
204
+ composed: true,
205
+ }));
206
+ }
207
+
208
+ _onKeyDown(e) {
209
+ const items = this._filteredItems;
210
+
211
+ switch (e.key) {
212
+ case 'ArrowDown':
213
+ e.preventDefault();
214
+ if (!this._open) { this._open = true; return; }
215
+ this._activeIndex = Math.min(this._activeIndex + 1, items.length - 1);
216
+ break;
217
+ case 'ArrowUp':
218
+ e.preventDefault();
219
+ this._activeIndex = Math.max(this._activeIndex - 1, 0);
220
+ break;
221
+ case 'Enter':
222
+ e.preventDefault();
223
+ if (this._activeIndex >= 0 && items[this._activeIndex]) {
224
+ this._selectItem(items[this._activeIndex]);
225
+ }
226
+ break;
227
+ case 'Escape':
228
+ this._open = false;
229
+ this._activeIndex = -1;
230
+ break;
231
+ }
232
+ }
233
+
234
+ updated(changed) {
235
+ if (changed.has('value') && !this._open) {
236
+ const item = this._normalizedItems.find(i => i.value === this.value);
237
+ if (item) this._query = item.label;
238
+ }
239
+ }
240
+
241
+ render() {
242
+ const filtered = this._filteredItems;
243
+ const listboxId = `${this._comboId}-listbox`;
244
+ const activeId = this._activeIndex >= 0 ? `${this._comboId}-opt-${this._activeIndex}` : undefined;
245
+
246
+ return html`
247
+ <div class="combobox__slot-host">
248
+ <slot @slotchange=${this._onSlotChange}></slot>
249
+ </div>
250
+ ${this.label ? html`<label class="combobox__label" for="${this._comboId}-input" part="label">${this.label}</label>` : ''}
251
+ <div class="combobox__wrapper" part="wrapper">
252
+ <input
253
+ id="${this._comboId}-input"
254
+ class="combobox__input"
255
+ type="text"
256
+ role="combobox"
257
+ autocomplete="off"
258
+ .value=${this._query}
259
+ placeholder=${this.placeholder}
260
+ ?disabled=${this.disabled}
261
+ aria-expanded=${String(this._open)}
262
+ aria-controls=${listboxId}
263
+ aria-activedescendant=${activeId || ''}
264
+ aria-autocomplete="list"
265
+ @input=${this._onInput}
266
+ @focus=${this._onFocus}
267
+ @keydown=${this._onKeyDown}
268
+ part="input"
269
+ />
270
+ <div
271
+ id=${listboxId}
272
+ class="combobox__listbox ${this._open ? 'combobox__listbox--open' : ''}"
273
+ role="listbox"
274
+ part="listbox"
275
+ >
276
+ ${filtered.length > 0
277
+ ? filtered.map((item, i) => html`
278
+ <button
279
+ id="${this._comboId}-opt-${i}"
280
+ class="combobox__option ${i === this._activeIndex ? 'combobox__option--active' : ''} ${item.value === this.value ? 'combobox__option--selected' : ''}"
281
+ role="option"
282
+ aria-selected=${item.value === this.value ? 'true' : 'false'}
283
+ @click=${() => this._selectItem(item)}
284
+ part="option"
285
+ >${item.label}</button>
286
+ `)
287
+ : html`<div class="combobox__empty">No results found</div>`
288
+ }
289
+ </div>
290
+ </div>
291
+ `;
292
+ }
293
+ }
294
+
295
+ customElements.define('arc-combobox', ArcCombobox);
@@ -0,0 +1,144 @@
1
+ import { LitElement, html, css } from 'lit';
2
+ import { tokenStyles } from '../shared-styles.js';
3
+
4
+ export class ArcCopyButton extends LitElement {
5
+ static properties = {
6
+ value: { type: String },
7
+ disabled: { type: Boolean, reflect: true },
8
+ _copied: { state: true },
9
+ };
10
+
11
+ static styles = [
12
+ tokenStyles,
13
+ css`
14
+ :host { display: inline-flex; }
15
+ :host([disabled]) { pointer-events: none; opacity: 0.4; }
16
+
17
+ .copy-btn {
18
+ display: inline-flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ gap: var(--space-xs);
22
+ background: rgba(var(--black-rgb), 0.6);
23
+ -webkit-backdrop-filter: blur(8px);
24
+ backdrop-filter: blur(8px);
25
+ border: 1px solid var(--border-default);
26
+ border-radius: var(--radius-sm);
27
+ color: var(--text-muted);
28
+ cursor: pointer;
29
+ padding: var(--touch-pad) var(--space-sm);
30
+ min-height: var(--touch-min);
31
+ font-family: var(--font-body);
32
+ font-size: var(--text-xs);
33
+ line-height: 1;
34
+ transition:
35
+ background var(--transition-fast),
36
+ border-color var(--transition-fast),
37
+ color var(--transition-fast),
38
+ box-shadow var(--transition-fast);
39
+ }
40
+
41
+ .copy-btn:hover {
42
+ border-color: var(--border-bright);
43
+ color: var(--text-primary);
44
+ background: var(--bg-hover);
45
+ }
46
+
47
+ .copy-btn:focus-visible {
48
+ outline: none;
49
+ box-shadow: var(--focus-glow);
50
+ }
51
+
52
+ .copy-btn.is-copied {
53
+ border-color: var(--color-success);
54
+ color: var(--color-success);
55
+ }
56
+
57
+ .copy-btn__icon {
58
+ display: inline-flex;
59
+ align-items: center;
60
+ justify-content: center;
61
+ width: 16px;
62
+ height: 16px;
63
+ }
64
+
65
+ .copy-btn__label {
66
+ user-select: none;
67
+ }
68
+
69
+ @media (prefers-reduced-motion: reduce) {
70
+ :host *,
71
+ :host *::before,
72
+ :host *::after {
73
+ animation-duration: 0.01ms !important;
74
+ animation-iteration-count: 1 !important;
75
+ transition-duration: 0.01ms !important;
76
+ }
77
+ }
78
+ `,
79
+ ];
80
+
81
+ constructor() {
82
+ super();
83
+ this.value = '';
84
+ this.disabled = false;
85
+ this._copied = false;
86
+ this._timeout = null;
87
+ }
88
+
89
+ disconnectedCallback() {
90
+ super.disconnectedCallback();
91
+ if (this._timeout) {
92
+ clearTimeout(this._timeout);
93
+ }
94
+ }
95
+
96
+ async _copy() {
97
+ if (this.disabled || this._copied) return;
98
+
99
+ try {
100
+ await navigator.clipboard.writeText(this.value);
101
+ this._copied = true;
102
+
103
+ this.dispatchEvent(new CustomEvent('arc-copy', {
104
+ detail: { value: this.value },
105
+ bubbles: true,
106
+ composed: true,
107
+ }));
108
+
109
+ this._timeout = setTimeout(() => {
110
+ this._copied = false;
111
+ }, 2000);
112
+ } catch {
113
+ // Clipboard API may fail in non-secure contexts
114
+ }
115
+ }
116
+
117
+ render() {
118
+ return html`
119
+ <button
120
+ class="copy-btn ${this._copied ? 'is-copied' : ''}"
121
+ @click=${this._copy}
122
+ ?disabled=${this.disabled}
123
+ aria-label=${this._copied ? 'Copied' : 'Copy to clipboard'}
124
+ part="button"
125
+ >
126
+ <span class="copy-btn__icon" part="icon">
127
+ ${this._copied ? html`
128
+ <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
129
+ <path d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06l2.5 2.5 6.72-6.72a.75.75 0 011.06 0z"/>
130
+ </svg>
131
+ ` : html`
132
+ <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
133
+ <path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"/>
134
+ <path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"/>
135
+ </svg>
136
+ `}
137
+ </span>
138
+ <span class="copy-btn__label" part="label">${this._copied ? 'Copied!' : 'Copy'}</span>
139
+ </button>
140
+ `;
141
+ }
142
+ }
143
+
144
+ customElements.define('arc-copy-button', ArcCopyButton);