@ahriknow/lux 0.0.1

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 (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +127 -0
  3. package/README_zh-CN.md +127 -0
  4. package/dist/components/lux-button/index.iife.min.js +292 -0
  5. package/dist/components/lux-button/index.min.js +292 -0
  6. package/dist/components/lux-code/index.iife.min.js +290 -0
  7. package/dist/components/lux-code/index.min.js +290 -0
  8. package/dist/components/lux-dropdown/index.iife.min.js +162 -0
  9. package/dist/components/lux-dropdown/index.min.js +162 -0
  10. package/dist/components/lux-example/index.iife.min.js +88 -0
  11. package/dist/components/lux-example/index.min.js +88 -0
  12. package/dist/components/lux-icon/index.iife.min.js +22 -0
  13. package/dist/components/lux-icon/index.min.js +22 -0
  14. package/dist/components/lux-input/index.iife.min.js +238 -0
  15. package/dist/components/lux-input/index.min.js +238 -0
  16. package/dist/components/lux-layout/index.iife.min.js +90 -0
  17. package/dist/components/lux-layout/index.min.js +90 -0
  18. package/dist/components/lux-menu/index.iife.min.js +193 -0
  19. package/dist/components/lux-menu/index.min.js +193 -0
  20. package/dist/components/lux-scroll/index.iife.min.js +137 -0
  21. package/dist/components/lux-scroll/index.min.js +137 -0
  22. package/dist/components/lux-switch/index.iife.min.js +116 -0
  23. package/dist/components/lux-switch/index.min.js +116 -0
  24. package/dist/components/lux-table/index.iife.min.js +67 -0
  25. package/dist/components/lux-table/index.min.js +67 -0
  26. package/dist/lux.core.min.js +1 -0
  27. package/dist/lux.i18n.min.js +1 -0
  28. package/dist/lux.iife.js +1822 -0
  29. package/dist/lux.iife.js.map +1 -0
  30. package/dist/lux.iife.min.js +1 -0
  31. package/dist/lux.js +1792 -0
  32. package/dist/lux.js.map +1 -0
  33. package/dist/lux.min.js +1 -0
  34. package/dist/lux.router.min.js +1 -0
  35. package/dist/lux.template.min.js +1 -0
  36. package/dist/lux.theme.min.js +1 -0
  37. package/dist/themes/dark.css +130 -0
  38. package/dist/themes/light.css +128 -0
  39. package/package.json +64 -0
  40. package/src/components/lux-button/index.js +319 -0
  41. package/src/components/lux-code/index.js +382 -0
  42. package/src/components/lux-dropdown/index.js +256 -0
  43. package/src/components/lux-example/index.js +117 -0
  44. package/src/components/lux-icon/index.js +180 -0
  45. package/src/components/lux-input/index.js +363 -0
  46. package/src/components/lux-layout/index.js +222 -0
  47. package/src/components/lux-menu/index.js +283 -0
  48. package/src/components/lux-scroll/index.js +349 -0
  49. package/src/components/lux-switch/index.js +203 -0
  50. package/src/components/lux-table/index.js +105 -0
  51. package/src/core.js +7 -0
  52. package/src/element.js +477 -0
  53. package/src/i18n/format.js +108 -0
  54. package/src/i18n/index.js +102 -0
  55. package/src/i18n/locale.js +26 -0
  56. package/src/index.js +22 -0
  57. package/src/router.js +330 -0
  58. package/src/template.js +402 -0
  59. package/src/theme/color.js +148 -0
  60. package/src/theme/create.js +97 -0
  61. package/src/theme/index.js +2 -0
  62. package/src/theme/tokens.js +128 -0
  63. package/src/themes/dark.css +130 -0
  64. package/src/themes/light.css +128 -0
@@ -0,0 +1,117 @@
1
+ import { html, css, LuxElement, registerComponent } from '../../index.js';
2
+
3
+ const styles = css`
4
+ :host {
5
+ display: block;
6
+ border: 1px solid rgb(var(--lux-border, 51 65 85));
7
+ border-radius: var(--lux-radius, 6px);
8
+ background: rgb(var(--lux-card, 30 41 59));
9
+ margin-bottom: var(--lux-space-4, 1rem);
10
+ }
11
+
12
+ .header {
13
+ display: flex;
14
+ align-items: center;
15
+ justify-content: space-between;
16
+ padding: 12px 16px;
17
+ border-bottom: 1px solid rgb(var(--lux-border, 51 65 85));
18
+ cursor: pointer;
19
+ user-select: none;
20
+ transition: background var(--lux-transition, 150ms ease);
21
+ }
22
+ .header:hover {
23
+ background: rgba(0, 0, 0, 0.03);
24
+ }
25
+
26
+ .title {
27
+ font-size: var(--lux-font-sm, 0.875rem);
28
+ font-weight: 600;
29
+ color: rgb(var(--lux-text, 241 245 249));
30
+ }
31
+
32
+ .toggle-btn {
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ width: 24px;
37
+ height: 24px;
38
+ border: none;
39
+ border-radius: var(--lux-radius-sm, 4px);
40
+ background: transparent;
41
+ color: rgb(var(--lux-text-secondary, 148 163 184));
42
+ cursor: pointer;
43
+ transition: all var(--lux-transition, 150ms ease);
44
+ font-size: 14px;
45
+ }
46
+ .toggle-btn:hover {
47
+ background: rgba(0, 0, 0, 0.05);
48
+ color: rgb(var(--lux-text, 241 245 249));
49
+ }
50
+ .toggle-icon {
51
+ transition: transform var(--lux-transition, 150ms ease);
52
+ display: inline-block;
53
+ }
54
+ :host([expanded]) .toggle-icon {
55
+ transform: rotate(180deg);
56
+ }
57
+
58
+ .main {
59
+ padding: 16px;
60
+ }
61
+
62
+ .footer {
63
+ padding: 12px 16px;
64
+ border-top: 1px solid rgb(var(--lux-border, 51 65 85));
65
+ background: rgb(var(--lux-bg, 248 250 252));
66
+ color: rgb(var(--lux-text-secondary, 148 163 184));
67
+ font-size: var(--lux-font-sm, 0.875rem);
68
+ display: none;
69
+ }
70
+
71
+ :host([expanded]) .footer {
72
+ display: block;
73
+ }
74
+ `;
75
+
76
+ class LuxExample extends LuxElement {
77
+ static styles = styles;
78
+
79
+ static properties = {
80
+ expanded: { type: Boolean, reflect: true },
81
+ };
82
+
83
+ constructor() {
84
+ super();
85
+ this.expanded = false;
86
+ }
87
+
88
+ _toggle() {
89
+ this.expanded = !this.expanded;
90
+ }
91
+
92
+ render() {
93
+ return html`
94
+ <div class="header" @click=${() => this._toggle()}>
95
+ <span class="title"><slot name="heading"></slot></span>
96
+ <button
97
+ class="toggle-btn"
98
+ @click=${(e) => {
99
+ e.stopPropagation();
100
+ this._toggle();
101
+ }}
102
+ >
103
+ <span class="toggle-icon">▼</span>
104
+ </button>
105
+ </div>
106
+ <div class="main">
107
+ <slot name="main"></slot>
108
+ </div>
109
+ <div class="footer">
110
+ <slot name="footer"></slot>
111
+ </div>
112
+ `;
113
+ }
114
+ }
115
+
116
+ registerComponent('lux-example', LuxExample);
117
+ export default LuxExample;
@@ -0,0 +1,180 @@
1
+ /**
2
+ * lux-icon — SVG icon component.
3
+ *
4
+ * Three modes:
5
+ * 1. Named icon: <lux-icon name="check"></lux-icon>
6
+ * 2. Custom SVG: <lux-icon><svg>...</svg></lux-icon>
7
+ * 3. Text fallback: <lux-icon name="?"</lux-icon>
8
+ *
9
+ * Props:
10
+ * name — icon name from registry
11
+ * size — icon size (default 1em)
12
+ * color — icon color (default 'currentColor')
13
+ */
14
+
15
+ import { html, css, LuxElement, registerComponent } from '../../index.js';
16
+
17
+ // ─── Built-in icon registry ───
18
+ const icons = {
19
+ check: '<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"/>',
20
+ content_copy:
21
+ '<path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/>',
22
+ close: '<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/>',
23
+ search: '<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>',
24
+ menu: '<path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/>',
25
+ arrow_back: '<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/>',
26
+ arrow_forward: '<path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"/>',
27
+ add: '<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>',
28
+ remove: '<path d="M19 13H5v-2h14v2z"/>',
29
+ edit: '<path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>',
30
+ delete: '<path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/>',
31
+ home: '<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>',
32
+ settings:
33
+ '<path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.07.62-.07.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>',
34
+ star: '<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/>',
35
+ heart: '<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>',
36
+ share: '<path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z"/>',
37
+ link: '<path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.1 0-2-.9-2-2s.9-2 2-2h4v-1.9H7c-2.76 0-5 2.24-5 5zm7-3h4v1.9h-4V9zm3.9 6h-4v1.9h4c1.1 0 2-.9 2-2s-.9-2-2-2h-4v-1.9h4c2.76 0 5 2.24 5 5s-2.24 5-5 5z"/>',
38
+ download: '<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>',
39
+ upload: '<path d="M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z"/>',
40
+ refresh:
41
+ '<path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.73 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/>',
42
+ more_vert:
43
+ '<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>',
44
+ more_horiz:
45
+ '<path d="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm8 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm8 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>',
46
+ expand_more: '<path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"/>',
47
+ expand_less: '<path d="M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z"/>',
48
+ visibility:
49
+ '<path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/>',
50
+ visibility_off:
51
+ '<path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.12 2.12C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.21.53-2.76 0-5-2.24-5-5 0-.8.2-1.54.53-2.21zm4.21-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/>',
52
+ info: '<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>',
53
+ warning: '<path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/>',
54
+ error_outline:
55
+ '<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>',
56
+ help_outline:
57
+ '<path d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 1-2.5 3-5V8c-1.1 0-2-.9-2-2zm0 10c-.59 0-1-.51-1-1s.41-1 1-1 1 .51 1 1-.41 1-1 1z"/>',
58
+ light_mode:
59
+ '<path d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41L5.99 4.58zm12.37 12.37c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0 .39-.39.39-1.03 0-1.41l-1.06-1.06zm1.06-10.96c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06zM7.05 18.36c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06z"/>',
60
+ dark_mode:
61
+ '<path d="M9.37 5.51c-.18.64-.27 1.31-.27 1.99 0 4.08 3.32 7.4 7.4 7.4.68 0 1.35-.09 1.99-.27C17.45 17.19 14.93 19 12 19c-3.86 0-7-3.14-7-7 0-2.93 1.81-5.45 4.37-6.49zM12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-.46-.04-.92-.1-1.36-.98 1.37-2.58 2.26-4.4 2.26-2.98 0-5.4-2.42-5.4-5.4 0-1.81.89-3.42 2.26-4.4-.44-.06-.9-.1-1.36-.1z"/>',
62
+ // Layout & navigation
63
+ view_quilt:
64
+ '<path d="M3 3v8h8V3H3zm6 6H5V5h4v4zm-6 4v8h8v-8H3zm6 6H5v-4h4v4zm4-16v8h8V3h-8zm6 6h-4V5h4v4zm-6 4v8h8v-8h-8zm6 6h-4v-4h4v4z"/>',
65
+ code: '<path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0L19.2 12l-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/>',
66
+ toggle_on:
67
+ '<path d="M6 7h12a5 5 0 010 10H6a5 5 0 010-10z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><circle cx="18" cy="12" r="3"/>',
68
+ swap_vert: '<path d="M16 17.01V10h-2v7.01h-3L15 21l4-3.99h-3zM9 3L5 6.99h3V14h2V6.99h3L9 3z"/>',
69
+ arrow_down: '<path d="M3 8l9 9 9-9H3z"/>',
70
+ language:
71
+ '<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95c-.32-1.25-.78-2.45-1.38-3.56 1.84.63 3.37 1.91 4.33 3.56zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2 0 .68.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56-1.84-.63-3.37-1.9-4.33-3.56zm2.95-8H5.08c.96-1.66 2.49-2.93 4.33-3.56C8.81 5.55 8.35 6.75 8.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2 0-.68.07-1.35.16-2h4.68c.09.65.16 1.32.16 2 0 .68-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95c-.96 1.65-2.49 2.93-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2 0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"/>',
72
+ smart_button:
73
+ '<path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/>',
74
+ table_chart:
75
+ '<path d="M3 3v18h18V3H3zm8 16H5v-6h6v6zm0-8H5V5h6v6zm8 8h-6v-6h6v6zm0-8h-6V5h6v6z"/>',
76
+ // Switch page
77
+ check_circle:
78
+ '<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>',
79
+ cancel: '<path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/>',
80
+ pause: '<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/>',
81
+ play: '<path d="M8 5v14l11-7z"/>',
82
+ stop: '<path d="M6 6h12v12H6z"/>',
83
+ save: '<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>',
84
+ folder: '<path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/>',
85
+ file: '<path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/>',
86
+ email: '<path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/>',
87
+ person: '<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>',
88
+ lock: '<path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/>',
89
+ filter: '<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>',
90
+ sort: '<path d="M3 18h6v-2H3v2zM3 6v2h18V6H3zm0 7h12v-2H3v2z"/>',
91
+ calendar:
92
+ '<path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11z"/>',
93
+ map: '<path d="M20.5 3l-.16.03L15 5.1 9 3 3.36 4.9c-.21.07-.36.25-.36.48V20.5c0 .28.22.5.5.5l.16-.03L9 18.9l6 2.1 5.64-1.9c.21-.07.36-.25.36-.48V3.5c0-.28-.22-.5-.5-.5zM15 19l-6-2.11V5l6 2.11V19z"/>',
94
+ camera: '<path d="M12 12m-3.2 0a3.2 3.2 0 1 0 6.4 0a3.2 3.2 0 1 0-6.4 0M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/>',
95
+ bookmark: '<path d="M17 3H7c-1.1 0-2 .9-2 2v16l7-3 7 3V5c0-1.1-.9-2-2-2z"/>',
96
+ clock: '<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z"/>',
97
+ chat: '<path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"/>',
98
+ phone: '<path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/>',
99
+ notification:
100
+ '<path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"/>',
101
+ cloud: '<path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z"/>',
102
+ download_done: '<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>',
103
+ github: '<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/>',
104
+ npm: '<path d="M1.763 0C.786 0 0 .786 0 1.763v20.474C0 23.214.786 24 1.763 24h20.474c.977 0 1.763-.786 1.763-1.763V1.763C24 .786 23.214 0 22.237 0zM5.13 5.323l13.837.019-.009 13.836h-3.464l.01-10.382h-3.456L12.04 19.17H5.113z"/>',
105
+ };
106
+
107
+ function registerIcons(map) {
108
+ Object.assign(icons, map);
109
+ }
110
+
111
+ const styles = css`
112
+ :host {
113
+ display: inline-flex;
114
+ align-items: center;
115
+ justify-content: center;
116
+ width: 1em;
117
+ height: 1em;
118
+ vertical-align: middle;
119
+ fill: currentColor;
120
+ flex-shrink: 0;
121
+ }
122
+
123
+ :host([size]) {
124
+ width: var(--icon-size);
125
+ height: var(--icon-size);
126
+ }
127
+
128
+ svg {
129
+ width: 100%;
130
+ height: 100%;
131
+ }
132
+ `;
133
+
134
+ class LuxIcon extends LuxElement {
135
+ static styles = styles;
136
+
137
+ static properties = {
138
+ name: { type: String, reflect: true },
139
+ size: { type: String, reflect: true },
140
+ color: { type: String, reflect: true },
141
+ };
142
+
143
+ constructor() {
144
+ super();
145
+ }
146
+
147
+ updated() {
148
+ if (this.size) {
149
+ this.style.setProperty('--icon-size', this._parseSize(this.size));
150
+ }
151
+ if (this.color) {
152
+ this.style.color = this.color;
153
+ }
154
+ const svg = this.renderRoot.querySelector('.icon-svg');
155
+ if (svg && this.name && icons[this.name]) {
156
+ svg.innerHTML = icons[this.name];
157
+ }
158
+ }
159
+
160
+ _parseSize(val) {
161
+ if (val == null) return '';
162
+ if (typeof val === 'number') return val + 'px';
163
+ if (/^\d+(\.\d+)?$/.test(val)) return val + 'px';
164
+ return val;
165
+ }
166
+
167
+ render() {
168
+ if (this.childNodes.length > 0) {
169
+ return html`<slot></slot>`;
170
+ }
171
+ if (this.name && icons[this.name]) {
172
+ return html`<svg class="icon-svg" viewBox="0 0 24 24"></svg>`;
173
+ }
174
+ return html`<span style="font-size:0.75em;font-weight:600">${this.name || '?'}</span>`;
175
+ }
176
+ }
177
+
178
+ registerComponent('lux-icon', LuxIcon);
179
+ export { LuxIcon, registerIcons, icons };
180
+ export default LuxIcon;
@@ -0,0 +1,363 @@
1
+ import { html, css, LuxElement, registerComponent } from '../../index.js';
2
+
3
+ const CLEAR_SVG = html`<svg viewBox="0 0 24 24" width="1em" height="1em" fill="currentColor">
4
+ <path
5
+ d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
6
+ />
7
+ </svg>`;
8
+
9
+ const styles = css`
10
+ :host {
11
+ display: inline-block;
12
+ width: 100%;
13
+ font-size: 13px;
14
+ vertical-align: middle;
15
+ }
16
+
17
+ .wrap {
18
+ display: flex;
19
+ align-items: center;
20
+ gap: 0;
21
+ border: 1px solid rgb(var(--lux-border, 226 232 240));
22
+ border-radius: var(--lux-radius, 6px);
23
+ background: rgb(var(--lux-card, 255 255 255));
24
+ transition:
25
+ border-color 0.2s,
26
+ box-shadow 0.2s;
27
+ overflow: hidden;
28
+ }
29
+
30
+ .wrap:hover {
31
+ border-color: rgb(var(--lux-primary-400, 108 92 231) / 40%);
32
+ }
33
+
34
+ :host([focused]) .wrap {
35
+ border-color: rgb(var(--lux-primary-400, 108 92 231));
36
+ box-shadow: 0 0 0 2px rgb(var(--lux-primary-400, 108 92 231) / 15%);
37
+ }
38
+
39
+ :host([has-error]) .wrap {
40
+ border-color: rgb(var(--lux-error, 239 68 68));
41
+ }
42
+
43
+ :host([has-error][focused]) .wrap {
44
+ box-shadow: 0 0 0 2px rgb(var(--lux-error, 239 68 68) / 15%);
45
+ }
46
+
47
+ /* Status colors */
48
+ :host([status='success']) .wrap {
49
+ border-color: rgb(var(--lux-success, 34 197 94));
50
+ }
51
+ :host([status='success'][focused]) .wrap {
52
+ box-shadow: 0 0 0 2px rgb(var(--lux-success, 34 197 94) / 15%);
53
+ }
54
+
55
+ :host([status='info']) .wrap {
56
+ border-color: rgb(var(--lux-info, 59 130 246));
57
+ }
58
+ :host([status='info'][focused]) .wrap {
59
+ box-shadow: 0 0 0 2px rgb(var(--lux-info, 59 130 246) / 15%);
60
+ }
61
+
62
+ :host([status='warning']) .wrap {
63
+ border-color: rgb(var(--lux-warning, 234 179 8));
64
+ }
65
+ :host([status='warning'][focused]) .wrap {
66
+ box-shadow: 0 0 0 2px rgb(var(--lux-warning, 234 179 8) / 15%);
67
+ }
68
+
69
+ :host([status='error']) .wrap {
70
+ border-color: rgb(var(--lux-error, 239 68 68));
71
+ }
72
+ :host([status='error'][focused]) .wrap {
73
+ box-shadow: 0 0 0 2px rgb(var(--lux-error, 239 68 68) / 15%);
74
+ }
75
+
76
+ /* Regex invalid (when no explicit status) */
77
+ :host([regex-invalid]) .wrap {
78
+ border-color: rgb(var(--lux-error, 239 68 68));
79
+ }
80
+ :host([regex-invalid][focused]) .wrap {
81
+ box-shadow: 0 0 0 2px rgb(var(--lux-error, 239 68 68) / 15%);
82
+ }
83
+ :host([data-regex-status='success'][regex-invalid]) .wrap {
84
+ border-color: rgb(var(--lux-success, 34 197 94));
85
+ }
86
+ :host([data-regex-status='info'][regex-invalid]) .wrap {
87
+ border-color: rgb(var(--lux-info, 59 130 246));
88
+ }
89
+ :host([data-regex-status='warning'][regex-invalid]) .wrap {
90
+ border-color: rgb(var(--lux-warning, 234 179 8));
91
+ }
92
+ :host([data-regex-status='error'][regex-invalid]) .wrap,
93
+ :host([data-regex-status=''][regex-invalid]) .wrap {
94
+ border-color: rgb(var(--lux-error, 239 68 68));
95
+ }
96
+
97
+ :host([disabled]) .wrap {
98
+ opacity: 0.5;
99
+ cursor: not-allowed;
100
+ pointer-events: none;
101
+ }
102
+
103
+ .prefix,
104
+ .suffix {
105
+ display: inline-flex;
106
+ align-items: center;
107
+ justify-content: center;
108
+ flex-shrink: 0;
109
+ color: rgb(var(--lux-text, 15 23 42));
110
+ padding: 0;
111
+ font-size: inherit;
112
+ line-height: 1;
113
+ user-select: none;
114
+ }
115
+
116
+ .prefix {
117
+ padding-left: 6px;
118
+ }
119
+ .suffix {
120
+ padding-right: 6px;
121
+ }
122
+
123
+ input {
124
+ flex: 1;
125
+ min-width: 0;
126
+ border: none;
127
+ outline: none;
128
+ background: transparent;
129
+ color: rgb(var(--lux-text, 15 23 42));
130
+ font-size: inherit;
131
+ font-family: inherit;
132
+ padding: 6px 8px;
133
+ line-height: 1.5;
134
+ width: 100%;
135
+ }
136
+
137
+ input[type='number'] {
138
+ -moz-appearance: textfield;
139
+ }
140
+
141
+ input[type='number']::-webkit-inner-spin-button,
142
+ input[type='number']::-webkit-outer-spin-button {
143
+ opacity: 1;
144
+ height: auto;
145
+ }
146
+
147
+ input::placeholder {
148
+ color: rgb(var(--lux-text-muted, 148 163 184));
149
+ }
150
+
151
+ input:disabled {
152
+ cursor: not-allowed;
153
+ }
154
+
155
+ .clear-btn {
156
+ display: none;
157
+ align-items: center;
158
+ justify-content: center;
159
+ flex-shrink: 0;
160
+ width: 20px;
161
+ height: 20px;
162
+ margin-right: 8px;
163
+ border: none;
164
+ border-radius: 50%;
165
+ background: transparent;
166
+ color: rgb(var(--lux-text-muted, 148 163 184));
167
+ cursor: pointer;
168
+ padding: 0;
169
+ transition:
170
+ color 0.15s,
171
+ background 0.15s;
172
+ }
173
+
174
+ .clear-btn:hover {
175
+ color: rgb(var(--lux-text, 15 23 42));
176
+ background: rgb(var(--lux-hover, 0 0 0 / 5%));
177
+ }
178
+
179
+ :host([clearable][has-value]:hover) .clear-btn,
180
+ :host([clearable][focused]) .clear-btn {
181
+ display: inline-flex;
182
+ }
183
+
184
+ .error-msg {
185
+ display: none;
186
+ margin-top: 4px;
187
+ font-size: 12px;
188
+ color: rgb(var(--lux-error, 239 68 68));
189
+ line-height: 1.4;
190
+ }
191
+
192
+ :host([has-error]) .error-msg {
193
+ display: block;
194
+ }
195
+
196
+ /* Size: sm */
197
+ :host([size='sm']) .wrap {
198
+ border-radius: var(--lux-radius-sm, 4px);
199
+ }
200
+ :host([size='sm']) input {
201
+ padding: 4px 8px;
202
+ font-size: 12px;
203
+ }
204
+ :host([size='sm']) .prefix {
205
+ padding-left: 4px;
206
+ }
207
+ :host([size='sm']) .suffix {
208
+ padding-right: 4px;
209
+ }
210
+
211
+ /* Size: lg */
212
+ :host([size='lg']) .wrap {
213
+ border-radius: var(--lux-radius, 6px);
214
+ }
215
+ :host([size='lg']) input {
216
+ padding: 10px 12px;
217
+ font-size: 15px;
218
+ }
219
+ :host([size='lg']) .prefix {
220
+ padding-left: 10px;
221
+ }
222
+ :host([size='lg']) .suffix {
223
+ padding-right: 10px;
224
+ }
225
+ `;
226
+
227
+ class LuxInput extends LuxElement {
228
+ static styles = styles;
229
+
230
+ static properties = {
231
+ value: { type: String, reflect: true },
232
+ placeholder: { type: String, reflect: true },
233
+ type: { type: String, reflect: true },
234
+ disabled: { type: Boolean, reflect: true },
235
+ clearable: { type: Boolean, reflect: true },
236
+ error: { type: String, reflect: true },
237
+ size: { type: String, reflect: true },
238
+ status: { type: String, reflect: true },
239
+ regex: { type: String, reflect: true },
240
+ regexStatus: { type: String, attribute: 'regex-status', reflect: true },
241
+ };
242
+
243
+ constructor() {
244
+ super();
245
+ this.value = '';
246
+ this.placeholder = '';
247
+ this.type = 'text';
248
+ this.disabled = false;
249
+ this.clearable = false;
250
+ this.error = '';
251
+ this.status = '';
252
+ this.regex = '';
253
+ this.regexStatus = '';
254
+ this._onDocClickBound = false;
255
+ }
256
+
257
+ connectedCallback() {
258
+ super.connectedCallback();
259
+ this._updateHasValue();
260
+ }
261
+
262
+ updated(changed) {
263
+ if (changed.has('value')) {
264
+ this._updateHasValue();
265
+ this._validateRegex();
266
+ }
267
+ if (changed.has('error')) {
268
+ if (this.error) {
269
+ this.setAttribute('has-error', '');
270
+ } else {
271
+ this.removeAttribute('has-error');
272
+ }
273
+ }
274
+ if (changed.has('status') || changed.has('regex') || changed.has('regexStatus')) {
275
+ this._validateRegex();
276
+ }
277
+ }
278
+
279
+ _updateHasValue() {
280
+ if (this.value) {
281
+ this.setAttribute('has-value', '');
282
+ } else {
283
+ this.removeAttribute('has-value');
284
+ }
285
+ }
286
+
287
+ _onInput(e) {
288
+ e.stopPropagation();
289
+ this.value = e.target.value;
290
+ this._validateRegex();
291
+ this.emit('input', { value: this.value });
292
+ }
293
+
294
+ _onChange(e) {
295
+ this.value = e.target.value;
296
+ this._validateRegex();
297
+ this.emit('change', { value: this.value });
298
+ }
299
+
300
+ _validateRegex() {
301
+ if (!this.regex || this.status) {
302
+ this.removeAttribute('regex-invalid');
303
+ this.removeAttribute('data-regex-status');
304
+ return;
305
+ }
306
+ if (!this.value) {
307
+ this.removeAttribute('regex-invalid');
308
+ this.removeAttribute('data-regex-status');
309
+ return;
310
+ }
311
+ const re = new RegExp(this.regex);
312
+ if (re.test(String(this.value))) {
313
+ this.removeAttribute('regex-invalid');
314
+ this.removeAttribute('data-regex-status');
315
+ } else {
316
+ this.setAttribute('regex-invalid', '');
317
+ this.setAttribute('data-regex-status', this.regexStatus || 'error');
318
+ }
319
+ }
320
+
321
+ _onFocus() {
322
+ this.setAttribute('focused', '');
323
+ this.emit('focus');
324
+ }
325
+
326
+ _onBlur() {
327
+ this.removeAttribute('focused');
328
+ this.emit('blur');
329
+ }
330
+
331
+ _clear() {
332
+ this.value = '';
333
+ this.removeAttribute('regex-invalid');
334
+ this.emit('input', { value: '' });
335
+ this.emit('clear');
336
+ const input = this.renderRoot.querySelector('input');
337
+ if (input) input.focus();
338
+ }
339
+
340
+ render() {
341
+ return html`
342
+ <div class="wrap">
343
+ <slot class="prefix" name="prefix"></slot>
344
+ <input
345
+ .type=${this.type}
346
+ .value=${this.value}
347
+ .placeholder=${this.placeholder}
348
+ .disabled=${this.disabled}
349
+ @input=${(e) => this._onInput(e)}
350
+ @change=${(e) => this._onChange(e)}
351
+ @focus=${() => this._onFocus()}
352
+ @blur=${() => this._onBlur()}
353
+ />
354
+ <span class="clear-btn" @click=${this._clear}>${CLEAR_SVG}</span>
355
+ <slot class="suffix" name="suffix"></slot>
356
+ </div>
357
+ ${this.error ? html`<span class="error-msg">${this.error}</span>` : ''}
358
+ `;
359
+ }
360
+ }
361
+
362
+ registerComponent('lux-input', LuxInput);
363
+ export default LuxInput;