@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,63 @@
1
+ import { LitElement, html, css, nothing } from 'lit';
2
+ import { tokenStyles } from '../shared-styles.js';
3
+
4
+ export class ArcHighlight extends LitElement {
5
+ static properties = {
6
+ text: { type: String },
7
+ query: { type: String },
8
+ caseSensitive: { type: Boolean, reflect: true, attribute: 'case-sensitive' },
9
+ };
10
+
11
+ static styles = [
12
+ tokenStyles,
13
+ css`
14
+ :host { display: inline; }
15
+
16
+ mark {
17
+ background: var(--accent-primary-subtle);
18
+ color: var(--accent-primary);
19
+ border-radius: var(--radius-sm);
20
+ padding: 1px var(--space-xs); /* cosmetic 1px vertical for inline highlight */
21
+ font-style: inherit;
22
+ font-weight: inherit;
23
+ }
24
+ `,
25
+ ];
26
+
27
+ constructor() {
28
+ super();
29
+ this.text = '';
30
+ this.query = '';
31
+ this.caseSensitive = false;
32
+ }
33
+
34
+ /** Escape regex special characters so arbitrary query strings are safe. */
35
+ _escapeRegex(str) {
36
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
37
+ }
38
+
39
+ render() {
40
+ if (!this.query || !this.text) {
41
+ return html`<span part="text">${this.text}</span>`;
42
+ }
43
+
44
+ const flags = this.caseSensitive ? 'g' : 'gi';
45
+ let regex;
46
+ try {
47
+ regex = new RegExp(`(${this._escapeRegex(this.query)})`, flags);
48
+ } catch {
49
+ return html`<span part="text">${this.text}</span>`;
50
+ }
51
+
52
+ const parts = this.text.split(regex);
53
+
54
+ // After split-by-captured-group, odd indices are matched segments.
55
+ return html`<span part="text">${parts.map((segment, i) =>
56
+ i % 2 === 1
57
+ ? html`<mark part="mark">${segment}</mark>`
58
+ : segment
59
+ )}</span>`;
60
+ }
61
+ }
62
+
63
+ customElements.define('arc-highlight', ArcHighlight);
@@ -0,0 +1,30 @@
1
+ import { LitElement } from 'lit';
2
+ import { iconRegistry } from './icon-registry.js';
3
+
4
+ export class ArcIconLibrary extends LitElement {
5
+ static properties = {
6
+ name: { type: String, reflect: true },
7
+ };
8
+
9
+ constructor() {
10
+ super();
11
+ this.name = 'phosphor';
12
+ }
13
+
14
+ connectedCallback() {
15
+ super.connectedCallback();
16
+ iconRegistry.use(this.name);
17
+ }
18
+
19
+ updated(changed) {
20
+ if (changed.has('name')) {
21
+ iconRegistry.use(this.name);
22
+ }
23
+ }
24
+
25
+ render() {
26
+ return undefined;
27
+ }
28
+ }
29
+
30
+ customElements.define('arc-icon-library', ArcIconLibrary);
@@ -0,0 +1,39 @@
1
+ import phosphorIcons from '../icons/phosphor.js';
2
+ import lucideIcons from '../icons/lucide.js';
3
+
4
+ const packs = { phosphor: phosphorIcons, lucide: lucideIcons };
5
+
6
+ let _active = null;
7
+
8
+ export const iconRegistry = {
9
+ /** Select a built-in icon library: 'phosphor' (default) or 'lucide'. */
10
+ use(library) {
11
+ const pack = packs[library];
12
+ if (!pack) throw new Error(`Unknown icon library "${library}". Use "phosphor" or "lucide".`);
13
+ _active = pack;
14
+ },
15
+
16
+ /** Register additional custom icons (merged on top of the active library). */
17
+ set(icons) {
18
+ if (!_active) _active = { ...packs.phosphor };
19
+ for (const [key, val] of Object.entries(icons)) {
20
+ const kebab = key
21
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
22
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
23
+ .toLowerCase();
24
+ _active[kebab] = val;
25
+ }
26
+ },
27
+
28
+ /** Look up an icon by kebab-case name. */
29
+ get(name) {
30
+ if (!_active) _active = packs.phosphor;
31
+ return _active[name] ?? null;
32
+ },
33
+
34
+ /** List all icon names in a library (defaults to active). */
35
+ list(library) {
36
+ const pack = library ? packs[library] : (_active || packs.phosphor);
37
+ return Object.keys(pack);
38
+ },
39
+ };
@@ -0,0 +1,95 @@
1
+ import { LitElement, html, css, nothing } from 'lit';
2
+ import { tokenStyles } from '../shared-styles.js';
3
+ import { iconRegistry } from './icon-registry.js';
4
+
5
+ const _svgCache = new Map();
6
+
7
+ /** Parse an SVG string into a sanitized SVG element, stripping scripts and event handlers. */
8
+ function sanitizeSvg(svgStr) {
9
+ if (_svgCache.has(svgStr)) return _svgCache.get(svgStr).cloneNode(true);
10
+ const doc = new DOMParser().parseFromString(svgStr, 'image/svg+xml');
11
+ const svg = doc.querySelector('svg');
12
+ if (!svg) return null;
13
+ // Strip <script> and any elements with event handler attributes
14
+ for (const el of svg.querySelectorAll('script')) el.remove();
15
+ const walk = svg.querySelectorAll('*');
16
+ for (const el of walk) {
17
+ for (const attr of [...el.attributes]) {
18
+ if (attr.name.startsWith('on')) el.removeAttribute(attr.name);
19
+ }
20
+ }
21
+ _svgCache.set(svgStr, svg);
22
+ return svg.cloneNode(true);
23
+ }
24
+
25
+ export class ArcIcon extends LitElement {
26
+ static properties = {
27
+ name: { type: String, reflect: true },
28
+ size: { type: String, reflect: true },
29
+ label: { type: String },
30
+ };
31
+
32
+ static styles = [
33
+ tokenStyles,
34
+ css`
35
+ :host {
36
+ display: inline-flex;
37
+ align-items: center;
38
+ justify-content: center;
39
+ color: currentColor;
40
+ vertical-align: middle;
41
+ }
42
+
43
+ :host([size="xs"]) { width: 12px; height: 12px; }
44
+ :host(:not([size])),
45
+ :host([size="sm"]) { width: 16px; height: 16px; }
46
+ :host([size="md"]) { width: 20px; height: 20px; }
47
+ :host([size="lg"]) { width: 24px; height: 24px; }
48
+ :host([size="xl"]) { width: 32px; height: 32px; }
49
+
50
+ .icon {
51
+ display: flex;
52
+ align-items: center;
53
+ justify-content: center;
54
+ width: 100%;
55
+ height: 100%;
56
+ }
57
+
58
+ svg {
59
+ width: 100%;
60
+ height: 100%;
61
+ }
62
+
63
+ ::slotted(svg) {
64
+ width: 100%;
65
+ height: 100%;
66
+ fill: currentColor;
67
+ }
68
+ `,
69
+ ];
70
+
71
+ constructor() {
72
+ super();
73
+ this.name = '';
74
+ this.size = 'sm';
75
+ this.label = '';
76
+ }
77
+
78
+ render() {
79
+ const svgStr = this.name ? iconRegistry.get(this.name) : null;
80
+ const svgNode = svgStr ? sanitizeSvg(svgStr) : null;
81
+ return html`
82
+ <span
83
+ class="icon"
84
+ role=${this.label ? 'img' : 'presentation'}
85
+ aria-label=${this.label || ''}
86
+ aria-hidden=${this.label ? 'false' : 'true'}
87
+ part="icon"
88
+ >
89
+ ${svgNode ?? html`<slot></slot>`}
90
+ </span>
91
+ `;
92
+ }
93
+ }
94
+
95
+ customElements.define('arc-icon', ArcIcon);
@@ -0,0 +1,44 @@
1
+ // ARC UI — Content tier
2
+ // Presentational and content display components
3
+
4
+ export { ArcAccordion } from './accordion.js';
5
+ export { ArcAccordionItem } from './accordion-item.js';
6
+ export { ArcAnimatedNumber } from './animated-number.js';
7
+ export { ArcAspectRatio } from './aspect-ratio.js';
8
+ export { ArcAvatar } from './avatar.js';
9
+ export { ArcAvatarGroup } from './avatar-group.js';
10
+ export { ArcBadge } from './badge.js';
11
+ export { ArcCallout } from './callout.js';
12
+ export { ArcCard } from './card.js';
13
+ export { ArcCarousel } from './carousel.js';
14
+ export { ArcCodeBlock } from './code-block.js';
15
+ export { ArcCollapsible } from './collapsible.js';
16
+ export { ArcColorSwatch } from './color-swatch.js';
17
+ export { ArcColumn } from './column.js';
18
+ export { ArcDataTable } from './data-table.js';
19
+ export { ArcDivider } from './divider.js';
20
+ export { ArcEmptyState } from './empty-state.js';
21
+ export { ArcFeatureCard } from './feature-card.js';
22
+ export { ArcHighlight } from './highlight.js';
23
+ export { ArcIcon } from './icon.js';
24
+ export { ArcIconLibrary } from './icon-library.js';
25
+ export { iconRegistry } from './icon-registry.js';
26
+ export { ArcInfiniteScroll } from './infinite-scroll.js';
27
+ export { ArcKbd } from './kbd.js';
28
+ export { ArcMarkdown } from './markdown.js';
29
+ export { ArcMarquee } from './marquee.js';
30
+ export { ArcMeter } from './meter.js';
31
+ export { ArcScrollArea } from './scroll-area.js';
32
+ export { ArcSkeleton } from './skeleton.js';
33
+ export { ArcSpinner } from './spinner.js';
34
+ export { ArcStack } from './stack.js';
35
+ export { ArcStat } from './stat.js';
36
+ export { ArcStep } from './step.js';
37
+ export { ArcStepper } from './stepper.js';
38
+ export { ArcTable } from './table.js';
39
+ export { ArcTag } from './tag.js';
40
+ export { ArcText } from './text.js';
41
+ export { ArcTimeline } from './timeline.js';
42
+ export { ArcTimelineItem } from './timeline-item.js';
43
+ export { ArcTruncate } from './truncate.js';
44
+ export { ArcValueCard } from './value-card.js';
@@ -0,0 +1,144 @@
1
+ import { LitElement, html, css } from 'lit';
2
+ import { tokenStyles } from '../shared-styles.js';
3
+ import '../content/spinner.js';
4
+
5
+ export class ArcInfiniteScroll extends LitElement {
6
+ static properties = {
7
+ threshold: { type: Number },
8
+ loading: { type: Boolean, reflect: true },
9
+ finished: { type: Boolean, reflect: true },
10
+ disabled: { type: Boolean, reflect: true },
11
+ };
12
+
13
+ static styles = [
14
+ tokenStyles,
15
+ css`
16
+ :host {
17
+ display: block;
18
+ }
19
+ :host([disabled]) { pointer-events: none; opacity: 0.4; }
20
+
21
+ .infinite-scroll {
22
+ display: flex;
23
+ flex-direction: column;
24
+ }
25
+
26
+ .infinite-scroll__content {
27
+ display: contents;
28
+ }
29
+
30
+ .infinite-scroll__footer {
31
+ display: flex;
32
+ align-items: center;
33
+ justify-content: center;
34
+ padding: var(--space-lg) 0;
35
+ min-height: 48px;
36
+ }
37
+
38
+ .infinite-scroll__sentinel {
39
+ width: 100%;
40
+ height: 1px;
41
+ pointer-events: none;
42
+ }
43
+
44
+ .infinite-scroll__end-text {
45
+ font-family: var(--font-body);
46
+ font-size: var(--body-size);
47
+ color: var(--text-muted);
48
+ text-align: center;
49
+ user-select: none;
50
+ }
51
+ `,
52
+ ];
53
+
54
+ constructor() {
55
+ super();
56
+ this.threshold = 200;
57
+ this.loading = false;
58
+ this.finished = false;
59
+ this.disabled = false;
60
+ this._observer = null;
61
+ }
62
+
63
+ firstUpdated() {
64
+ this._setupObserver();
65
+ }
66
+
67
+ updated(changed) {
68
+ if (changed.has('finished') || changed.has('disabled')) {
69
+ this._setupObserver();
70
+ }
71
+ }
72
+
73
+ disconnectedCallback() {
74
+ super.disconnectedCallback();
75
+ this._destroyObserver();
76
+ }
77
+
78
+ _setupObserver() {
79
+ this._destroyObserver();
80
+
81
+ if (this.finished || this.disabled) return;
82
+
83
+ const sentinel = this.shadowRoot.querySelector('.infinite-scroll__sentinel');
84
+ if (!sentinel) return;
85
+
86
+ this._observer = new IntersectionObserver(
87
+ (entries) => {
88
+ const entry = entries[0];
89
+ if (entry && entry.isIntersecting && !this.loading && !this.finished && !this.disabled) {
90
+ this.dispatchEvent(new CustomEvent('arc-load-more', {
91
+ bubbles: true,
92
+ composed: true,
93
+ }));
94
+ }
95
+ },
96
+ {
97
+ rootMargin: `0px 0px ${this.threshold}px 0px`,
98
+ },
99
+ );
100
+
101
+ this._observer.observe(sentinel);
102
+ }
103
+
104
+ _destroyObserver() {
105
+ if (this._observer) {
106
+ this._observer.disconnect();
107
+ this._observer = null;
108
+ }
109
+ }
110
+
111
+ render() {
112
+ return html`
113
+ <div
114
+ class="infinite-scroll"
115
+ role="feed"
116
+ aria-busy=${this.loading ? 'true' : 'false'}
117
+ part="container"
118
+ >
119
+ <div class="infinite-scroll__content" part="content">
120
+ <slot></slot>
121
+ </div>
122
+
123
+ ${this.finished
124
+ ? html`
125
+ <div class="infinite-scroll__footer" part="footer">
126
+ <span class="infinite-scroll__end-text" part="end-text">No more items</span>
127
+ </div>
128
+ `
129
+ : html`
130
+ <div class="infinite-scroll__footer" part="footer">
131
+ ${this.loading
132
+ ? html`<arc-spinner size="sm" part="spinner"></arc-spinner>`
133
+ : ''
134
+ }
135
+ </div>
136
+ <div class="infinite-scroll__sentinel"></div>
137
+ `
138
+ }
139
+ </div>
140
+ `;
141
+ }
142
+ }
143
+
144
+ customElements.define('arc-infinite-scroll', ArcInfiniteScroll);
@@ -0,0 +1,40 @@
1
+ import { LitElement, html, css } from 'lit';
2
+ import { tokenStyles } from '../shared-styles.js';
3
+
4
+ export class ArcKbd extends LitElement {
5
+ static properties = {};
6
+
7
+ static styles = [
8
+ tokenStyles,
9
+ css`
10
+ :host { display: inline; }
11
+
12
+ .kbd {
13
+ display: inline-flex;
14
+ align-items: center;
15
+ font-family: var(--font-mono);
16
+ font-size: var(--text-sm);
17
+ line-height: 1;
18
+ color: var(--text-secondary);
19
+ background: var(--bg-elevated);
20
+ border: 1px solid var(--border-default);
21
+ border-bottom-width: 2px;
22
+ border-radius: var(--radius-sm);
23
+ padding: 2px calc(var(--space-xs) + 2px); /* cosmetic 2px vertical for kbd element */
24
+ white-space: nowrap;
25
+ user-select: none;
26
+ vertical-align: baseline;
27
+ }
28
+ `,
29
+ ];
30
+
31
+ constructor() {
32
+ super();
33
+ }
34
+
35
+ render() {
36
+ return html`<kbd class="kbd" part="kbd"><slot></slot></kbd>`;
37
+ }
38
+ }
39
+
40
+ customElements.define('arc-kbd', ArcKbd);