@sondor-technology/sondor-components 1.0.0 → 1.0.1-develop.2

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 (168) hide show
  1. package/README.md +8 -0
  2. package/dist/components/alert/alert.d.ts +13 -0
  3. package/dist/components/alert/alert.d.ts.map +1 -0
  4. package/dist/components/alert/alert.js +105 -0
  5. package/dist/components/badge/badge.d.ts +13 -0
  6. package/dist/components/badge/badge.d.ts.map +1 -0
  7. package/dist/components/badge/badge.js +101 -0
  8. package/dist/components/breadcrumb/breadcrumb.d.ts +19 -0
  9. package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -0
  10. package/dist/components/breadcrumb/breadcrumb.js +99 -0
  11. package/dist/components/button/button.d.ts +23 -0
  12. package/dist/components/button/button.d.ts.map +1 -0
  13. package/dist/components/button/button.js +304 -0
  14. package/dist/components/button-group/button-group.d.ts +6 -0
  15. package/dist/components/button-group/button-group.d.ts.map +1 -0
  16. package/dist/components/button-group/button-group.js +74 -0
  17. package/dist/components/card/card.d.ts +11 -0
  18. package/dist/components/card/card.d.ts.map +1 -0
  19. package/dist/components/card/card.js +54 -0
  20. package/dist/components/checkbox/checkbox.d.ts +28 -0
  21. package/dist/components/checkbox/checkbox.d.ts.map +1 -0
  22. package/dist/components/checkbox/checkbox.js +247 -0
  23. package/dist/components/code-block/code-block.d.ts +20 -0
  24. package/dist/components/code-block/code-block.d.ts.map +1 -0
  25. package/dist/components/code-block/code-block.js +260 -0
  26. package/dist/components/content-card/content-card.d.ts +13 -0
  27. package/dist/components/content-card/content-card.d.ts.map +1 -0
  28. package/dist/components/content-card/content-card.js +140 -0
  29. package/dist/components/divider/divider.d.ts +16 -0
  30. package/dist/components/divider/divider.d.ts.map +1 -0
  31. package/dist/components/divider/divider.js +82 -0
  32. package/dist/components/drawer/drawer.d.ts +22 -0
  33. package/dist/components/drawer/drawer.d.ts.map +1 -0
  34. package/dist/components/drawer/drawer.js +226 -0
  35. package/dist/components/field/field.d.ts +11 -0
  36. package/dist/components/field/field.d.ts.map +1 -0
  37. package/dist/components/field/field.js +32 -0
  38. package/dist/components/footer/footer.d.ts +6 -0
  39. package/dist/components/footer/footer.d.ts.map +1 -0
  40. package/dist/components/footer/footer.js +65 -0
  41. package/dist/components/form/form-step.d.ts +13 -0
  42. package/dist/components/form/form-step.d.ts.map +1 -0
  43. package/dist/components/form/form-step.js +41 -0
  44. package/dist/components/form/form.d.ts +25 -0
  45. package/dist/components/form/form.d.ts.map +1 -0
  46. package/dist/components/form/form.js +358 -0
  47. package/dist/components/grid/grid-row.d.ts +17 -0
  48. package/dist/components/grid/grid-row.d.ts.map +1 -0
  49. package/dist/components/grid/grid-row.js +92 -0
  50. package/dist/components/grid/grid.d.ts +13 -0
  51. package/dist/components/grid/grid.d.ts.map +1 -0
  52. package/dist/components/grid/grid.js +42 -0
  53. package/dist/components/icon/icon-paths.d.ts +116 -0
  54. package/dist/components/icon/icon-paths.d.ts.map +1 -0
  55. package/dist/components/icon/icon-paths.js +121 -0
  56. package/dist/components/icon/icon.d.ts +16 -0
  57. package/dist/components/icon/icon.d.ts.map +1 -0
  58. package/dist/components/icon/icon.js +95 -0
  59. package/dist/components/input/input.d.ts +31 -0
  60. package/dist/components/input/input.d.ts.map +1 -0
  61. package/dist/components/input/input.js +268 -0
  62. package/dist/components/link/link.d.ts +16 -0
  63. package/dist/components/link/link.d.ts.map +1 -0
  64. package/dist/components/link/link.js +116 -0
  65. package/dist/components/metadata/metadata.d.ts +18 -0
  66. package/dist/components/metadata/metadata.d.ts.map +1 -0
  67. package/dist/components/metadata/metadata.js +71 -0
  68. package/dist/components/modal/modal.d.ts +24 -0
  69. package/dist/components/modal/modal.d.ts.map +1 -0
  70. package/dist/components/modal/modal.js +163 -0
  71. package/dist/components/page-layout/page-layout.d.ts +46 -0
  72. package/dist/components/page-layout/page-layout.d.ts.map +1 -0
  73. package/dist/components/page-layout/page-layout.js +697 -0
  74. package/dist/components/radio/radio.d.ts +16 -0
  75. package/dist/components/radio/radio.d.ts.map +1 -0
  76. package/dist/components/radio/radio.js +152 -0
  77. package/dist/components/radio-group/radio-group.d.ts +27 -0
  78. package/dist/components/radio-group/radio-group.d.ts.map +1 -0
  79. package/dist/components/radio-group/radio-group.js +161 -0
  80. package/dist/components/router/router.d.ts +7 -0
  81. package/dist/components/router/router.d.ts.map +1 -0
  82. package/dist/components/router/router.js +37 -0
  83. package/dist/components/select/select.d.ts +16 -0
  84. package/dist/components/select/select.d.ts.map +1 -0
  85. package/dist/components/select/select.js +172 -0
  86. package/dist/components/skeleton/skeleton.d.ts +17 -0
  87. package/dist/components/skeleton/skeleton.d.ts.map +1 -0
  88. package/dist/components/skeleton/skeleton.js +78 -0
  89. package/dist/components/switch/switch.d.ts +25 -0
  90. package/dist/components/switch/switch.d.ts.map +1 -0
  91. package/dist/components/switch/switch.js +185 -0
  92. package/dist/components/table/data-table.d.ts +72 -0
  93. package/dist/components/table/data-table.d.ts.map +1 -0
  94. package/dist/components/table/data-table.js +551 -0
  95. package/dist/components/table/table.d.ts +22 -0
  96. package/dist/components/table/table.d.ts.map +1 -0
  97. package/dist/components/table/table.js +167 -0
  98. package/dist/components/tabs/tab-panel.d.ts +8 -0
  99. package/dist/components/tabs/tab-panel.d.ts.map +1 -0
  100. package/dist/components/tabs/tab-panel.js +46 -0
  101. package/dist/components/tabs/tab.d.ts +9 -0
  102. package/dist/components/tabs/tab.d.ts.map +1 -0
  103. package/dist/components/tabs/tab.js +91 -0
  104. package/dist/components/tabs/tabs.d.ts +14 -0
  105. package/dist/components/tabs/tabs.d.ts.map +1 -0
  106. package/dist/components/tabs/tabs.js +85 -0
  107. package/dist/components/textarea/textarea.d.ts +36 -0
  108. package/dist/components/textarea/textarea.d.ts.map +1 -0
  109. package/dist/components/textarea/textarea.js +231 -0
  110. package/dist/components/theme-toggle/theme-toggle.d.ts +15 -0
  111. package/dist/components/theme-toggle/theme-toggle.d.ts.map +1 -0
  112. package/dist/components/theme-toggle/theme-toggle.js +130 -0
  113. package/dist/components/vertical-nav/vertical-nav-group.d.ts +9 -0
  114. package/dist/components/vertical-nav/vertical-nav-group.d.ts.map +1 -0
  115. package/dist/components/vertical-nav/vertical-nav-group.js +111 -0
  116. package/dist/components/vertical-nav/vertical-nav-item.d.ts +8 -0
  117. package/dist/components/vertical-nav/vertical-nav-item.d.ts.map +1 -0
  118. package/dist/components/vertical-nav/vertical-nav-item.js +65 -0
  119. package/dist/styles/index.css +171 -0
  120. package/dist/styles/reset.d.ts +2 -0
  121. package/dist/styles/reset.d.ts.map +1 -0
  122. package/dist/styles/reset.js +50 -0
  123. package/package.json +3 -2
  124. package/src/components/alert/alert.ts +100 -100
  125. package/src/components/badge/badge.ts +96 -96
  126. package/src/components/breadcrumb/breadcrumb.ts +98 -98
  127. package/src/components/button/button.ts +281 -281
  128. package/src/components/button-group/button-group.ts +67 -67
  129. package/src/components/card/card.ts +52 -52
  130. package/src/components/checkbox/checkbox.ts +238 -238
  131. package/src/components/code-block/code-block.ts +256 -256
  132. package/src/components/content-card/content-card.ts +133 -133
  133. package/src/components/divider/divider.ts +68 -68
  134. package/src/components/drawer/drawer.ts +217 -217
  135. package/src/components/field/field.ts +30 -30
  136. package/src/components/footer/footer.ts +58 -58
  137. package/src/components/form/form-step.ts +32 -32
  138. package/src/components/form/form.ts +376 -376
  139. package/src/components/grid/grid-row.ts +75 -75
  140. package/src/components/grid/grid.ts +36 -36
  141. package/src/components/icon/icon-paths.ts +124 -124
  142. package/src/components/icon/icon.ts +82 -82
  143. package/src/components/input/input.ts +248 -248
  144. package/src/components/link/link.ts +97 -97
  145. package/src/components/metadata/metadata.ts +66 -66
  146. package/src/components/modal/modal.ts +158 -158
  147. package/src/components/page-layout/page-layout.ts +704 -704
  148. package/src/components/radio/radio.ts +142 -142
  149. package/src/components/radio-group/radio-group.ts +157 -157
  150. package/src/components/router/router.ts +46 -46
  151. package/src/components/select/select.ts +161 -161
  152. package/src/components/skeleton/skeleton.ts +67 -67
  153. package/src/components/switch/switch.ts +181 -181
  154. package/src/components/table/data-table.ts +556 -556
  155. package/src/components/table/table.ts +153 -153
  156. package/src/components/tabs/tab-panel.ts +30 -30
  157. package/src/components/tabs/tab.ts +76 -76
  158. package/src/components/tabs/tabs.ts +88 -88
  159. package/src/components/textarea/textarea.ts +215 -215
  160. package/src/components/theme-toggle/theme-toggle.ts +131 -131
  161. package/src/components/vertical-nav/vertical-nav-group.ts +96 -96
  162. package/src/components/vertical-nav/vertical-nav-item.ts +49 -49
  163. package/src/styles/globals.css +58 -58
  164. package/src/styles/index.css +80 -80
  165. package/src/styles/reset.ts +50 -50
  166. package/src/styles/theme.css +58 -58
  167. package/src/styles/typography.css +25 -25
  168. package/tsconfig.json +15 -15
@@ -1,257 +1,257 @@
1
- import { LitElement, html, css } from 'lit';
2
- import { customElement, property, state, query } from 'lit/decorators.js';
3
- import { unsafeHTML } from 'lit/directives/unsafe-html.js';
4
- import { resetStyles } from '../../styles/reset.js';
5
-
6
- import Prism from 'prismjs';
7
- import 'prismjs/components/prism-typescript.js';
8
- import 'prismjs/components/prism-javascript.js';
9
- import 'prismjs/components/prism-css.js';
10
- import 'prismjs/components/prism-json.js';
11
- import 'prismjs/components/prism-bash.js';
12
- import 'prismjs/components/prism-csharp.js';
13
-
14
- interface CodeSnippet {
15
- lang: string;
16
- label: string;
17
- rawCode: string;
18
- highlightedCode: string;
19
- }
20
-
21
- @customElement('st-code-block')
22
- export class StCodeBlock extends LitElement {
23
- // Fallback language if a template doesn't provide one
24
- @property({ type: String }) language = 'typescript';
25
-
26
- @state() private _snippets: CodeSnippet[] = [];
27
- @state() private _activeIndex = 0;
28
- @state() private _copied = false;
29
-
30
- @query('slot[name="raw"]') private _slot!: HTMLSlotElement;
31
-
32
- static styles = [
33
- resetStyles,
34
- css`
35
- :host {
36
- display: block;
37
- margin: var(--st-spacing-md) 0;
38
- border-radius: 8px;
39
- background-color: var(--st-color-surface);
40
- border: 1px solid var(--st-color-border);
41
- overflow: hidden;
42
- }
43
-
44
- .header {
45
- display: flex;
46
- justify-content: space-between;
47
- align-items: center;
48
- background-color: rgba(0, 0, 0, 0.2);
49
- border-bottom: 1px solid var(--st-color-border);
50
- padding: 0 var(--st-spacing-md);
51
- min-height: 2.5rem; /* 40px */
52
- }
53
-
54
- /* Single language label */
55
- .language-label {
56
- font-size: var(--st-font-size-xs);
57
- color: var(--st-color-text-muted);
58
- text-transform: uppercase;
59
- letter-spacing: 0.05em;
60
- font-weight: var(--st-font-weight-bold);
61
- }
62
-
63
- /* Multi-language Tabs */
64
- .tabs {
65
- display: flex;
66
- gap: var(--st-spacing-md);
67
- height: 100%;
68
- }
69
-
70
- .tab {
71
- background: transparent;
72
- border: none;
73
- border-bottom: 2px solid transparent;
74
- color: var(--st-color-text-muted);
75
- font-size: var(--st-font-size-xs);
76
- font-weight: var(--st-font-weight-medium);
77
- padding: var(--st-spacing-xs) 0;
78
- cursor: pointer;
79
- transition: all 0.2s ease;
80
- }
81
-
82
- .tab:hover {
83
- color: var(--st-color-text);
84
- }
85
-
86
- .tab.active {
87
- color: var(--st-color-primary);
88
- border-bottom-color: var(--st-color-primary);
89
- }
90
-
91
- /* Copy Button */
92
- .copy-button {
93
- background: transparent;
94
- border: 1px solid transparent;
95
- color: var(--st-color-text-muted);
96
- cursor: pointer;
97
- display: flex;
98
- align-items: center;
99
- gap: var(--st-spacing-xs);
100
- padding: 4px 8px;
101
- border-radius: 4px;
102
- font-size: var(--st-font-size-xs);
103
- transition: all 0.2s ease;
104
- margin-left: auto; /* Pushes it to the right */
105
- }
106
-
107
- .copy-button:hover {
108
- color: var(--st-color-text);
109
- background-color: rgba(255, 255, 255, 0.05);
110
- }
111
-
112
- .copy-button svg {
113
- width: 14px;
114
- height: 14px;
115
- fill: none;
116
- stroke: currentColor;
117
- stroke-width: 2;
118
- stroke-linecap: round;
119
- stroke-linejoin: round;
120
- }
121
-
122
- .copy-button.copied {
123
- color: var(--st-color-success);
124
- }
125
-
126
- pre {
127
- margin: 0;
128
- padding: var(--st-spacing-md);
129
- overflow-x: auto;
130
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
131
- font-size: 0.875rem;
132
- line-height: 1.5;
133
- color: var(--st-color-text);
134
- }
135
-
136
- .raw-content {
137
- display: none;
138
- }
139
-
140
- /* ==========================================================
141
- Prism.js Theme Mapping (Space Grey / Purple Aesthetic)
142
- ========================================================== */
143
- .token.comment, .token.prolog, .token.doctype, .token.cdata { color: var(--st-color-text-muted); font-style: italic; }
144
- .token.punctuation { color: #8b949e; }
145
- .token.namespace { opacity: .7; }
146
- .token.property, .token.tag, .token.boolean, .token.number, .token.constant, .token.symbol, .token.deleted { color: #79c0ff; }
147
- .token.selector, .token.attr-name, .token.string, .token.char, .token.builtin, .token.inserted { color: #a5d6ff; }
148
- .token.operator, .token.entity, .token.url, .language-css .token.string, .style .token.string { color: var(--st-color-text); }
149
- .token.atrule, .token.attr-value, .token.keyword { color: var(--st-color-primary); }
150
- .token.function, .token.class-name { color: #d2a8ff; }
151
- .token.regex, .token.important, .token.variable { color: #ffa657; }
152
- `
153
- ];
154
-
155
- private _handleSlotChange() {
156
- const nodes = this._slot.assignedNodes({ flatten: true });
157
-
158
- // Find ALL template tags passed into the slot
159
- const templates = nodes.filter(n => n instanceof HTMLTemplateElement) as HTMLTemplateElement[];
160
-
161
- if (templates.length > 0) {
162
- // Map over every template and parse its code and attributes
163
- this._snippets = templates.map(template => {
164
- const lang = template.getAttribute('data-lang') || this.language;
165
- const label = template.getAttribute('data-label') || lang; // Fallback to lang if no label provided
166
- const rawCode = this._dedent(template.innerHTML);
167
-
168
- const grammar = Prism.languages[lang] || Prism.languages.javascript;
169
- const highlightedCode = Prism.highlight(rawCode, grammar, lang);
170
-
171
- return { lang, label, rawCode, highlightedCode };
172
- });
173
- } else {
174
- // Fallback: If no template is used, process raw text just like before
175
- const rawText = nodes.map(n => n.textContent).join('');
176
- const rawCode = this._dedent(rawText);
177
-
178
- if (rawCode) {
179
- const grammar = Prism.languages[this.language] || Prism.languages.javascript;
180
- const highlightedCode = Prism.highlight(rawCode, grammar, this.language);
181
- this._snippets = [{ lang: this.language, label: this.language, rawCode, highlightedCode }];
182
- }
183
- }
184
-
185
- // Reset to the first tab whenever new code is injected
186
- this._activeIndex = 0;
187
- }
188
-
189
- private _dedent(text: string): string {
190
- const lines = text.split('\n');
191
- const minIndent = lines
192
- .filter(line => line.trim().length > 0)
193
- .reduce((min, line) => {
194
- const indent = line.match(/^\s*/)?.[0].length || 0;
195
- return Math.min(min, indent);
196
- }, Infinity);
197
-
198
- if (minIndent === Infinity || minIndent === 0) return text.trim();
199
- return lines.map(line => line.slice(minIndent)).join('\n').trim();
200
- }
201
-
202
- private async _copyToClipboard() {
203
- // Copy the code from the currently active tab
204
- const activeSnippet = this._snippets[this._activeIndex];
205
- if (!activeSnippet) return;
206
-
207
- try {
208
- await navigator.clipboard.writeText(activeSnippet.rawCode);
209
- this._copied = true;
210
- setTimeout(() => { this._copied = false; }, 2000);
211
- } catch (err) {
212
- console.error('Failed to copy text: ', err);
213
- }
214
- }
215
-
216
- render() {
217
- const activeSnippet = this._snippets[this._activeIndex];
218
-
219
- return html`
220
- <div class="header">
221
- ${this._snippets.length > 1
222
- ? html`
223
- <div class="tabs">
224
- ${this._snippets.map((snippet, index) => html`
225
- <button
226
- class="tab ${index === this._activeIndex ? 'active' : ''}"
227
- @click=${() => this._activeIndex = index}
228
- >
229
- ${snippet.label}
230
- </button>
231
- `)}
232
- </div>
233
- `
234
- : html`
235
- <span class="language-label">${activeSnippet?.label || this.language}</span>
236
- `
237
- }
238
-
239
- <button class="copy-button ${this._copied ? 'copied' : ''}" @click=${this._copyToClipboard}>
240
- ${this._copied
241
- ? html`<svg viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"></polyline></svg> Copied!`
242
- : html`<svg viewBox="0 0 24 24"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> Copy`
243
- }
244
- </button>
245
- </div>
246
-
247
- <!-- Render the currently active snippet -->
248
- ${activeSnippet ? html`
249
- <pre><code class="language-${activeSnippet.lang}">${unsafeHTML(activeSnippet.highlightedCode)}</code></pre>
250
- ` : ''}
251
-
252
- <div class="raw-content">
253
- <slot name="raw" @slotchange=${this._handleSlotChange}></slot>
254
- </div>
255
- `;
256
- }
1
+ import { LitElement, html, css } from 'lit';
2
+ import { customElement, property, state, query } from 'lit/decorators.js';
3
+ import { unsafeHTML } from 'lit/directives/unsafe-html.js';
4
+ import { resetStyles } from '../../styles/reset.js';
5
+
6
+ import Prism from 'prismjs';
7
+ import 'prismjs/components/prism-typescript.js';
8
+ import 'prismjs/components/prism-javascript.js';
9
+ import 'prismjs/components/prism-css.js';
10
+ import 'prismjs/components/prism-json.js';
11
+ import 'prismjs/components/prism-bash.js';
12
+ import 'prismjs/components/prism-csharp.js';
13
+
14
+ interface CodeSnippet {
15
+ lang: string;
16
+ label: string;
17
+ rawCode: string;
18
+ highlightedCode: string;
19
+ }
20
+
21
+ @customElement('st-code-block')
22
+ export class StCodeBlock extends LitElement {
23
+ // Fallback language if a template doesn't provide one
24
+ @property({ type: String }) language = 'typescript';
25
+
26
+ @state() private _snippets: CodeSnippet[] = [];
27
+ @state() private _activeIndex = 0;
28
+ @state() private _copied = false;
29
+
30
+ @query('slot[name="raw"]') private _slot!: HTMLSlotElement;
31
+
32
+ static styles = [
33
+ resetStyles,
34
+ css`
35
+ :host {
36
+ display: block;
37
+ margin: var(--st-spacing-md) 0;
38
+ border-radius: 8px;
39
+ background-color: var(--st-color-surface);
40
+ border: 1px solid var(--st-color-border);
41
+ overflow: hidden;
42
+ }
43
+
44
+ .header {
45
+ display: flex;
46
+ justify-content: space-between;
47
+ align-items: center;
48
+ background-color: rgba(0, 0, 0, 0.2);
49
+ border-bottom: 1px solid var(--st-color-border);
50
+ padding: 0 var(--st-spacing-md);
51
+ min-height: 2.5rem; /* 40px */
52
+ }
53
+
54
+ /* Single language label */
55
+ .language-label {
56
+ font-size: var(--st-font-size-xs);
57
+ color: var(--st-color-text-muted);
58
+ text-transform: uppercase;
59
+ letter-spacing: 0.05em;
60
+ font-weight: var(--st-font-weight-bold);
61
+ }
62
+
63
+ /* Multi-language Tabs */
64
+ .tabs {
65
+ display: flex;
66
+ gap: var(--st-spacing-md);
67
+ height: 100%;
68
+ }
69
+
70
+ .tab {
71
+ background: transparent;
72
+ border: none;
73
+ border-bottom: 2px solid transparent;
74
+ color: var(--st-color-text-muted);
75
+ font-size: var(--st-font-size-xs);
76
+ font-weight: var(--st-font-weight-medium);
77
+ padding: var(--st-spacing-xs) 0;
78
+ cursor: pointer;
79
+ transition: all 0.2s ease;
80
+ }
81
+
82
+ .tab:hover {
83
+ color: var(--st-color-text);
84
+ }
85
+
86
+ .tab.active {
87
+ color: var(--st-color-primary);
88
+ border-bottom-color: var(--st-color-primary);
89
+ }
90
+
91
+ /* Copy Button */
92
+ .copy-button {
93
+ background: transparent;
94
+ border: 1px solid transparent;
95
+ color: var(--st-color-text-muted);
96
+ cursor: pointer;
97
+ display: flex;
98
+ align-items: center;
99
+ gap: var(--st-spacing-xs);
100
+ padding: 4px 8px;
101
+ border-radius: 4px;
102
+ font-size: var(--st-font-size-xs);
103
+ transition: all 0.2s ease;
104
+ margin-left: auto; /* Pushes it to the right */
105
+ }
106
+
107
+ .copy-button:hover {
108
+ color: var(--st-color-text);
109
+ background-color: rgba(255, 255, 255, 0.05);
110
+ }
111
+
112
+ .copy-button svg {
113
+ width: 14px;
114
+ height: 14px;
115
+ fill: none;
116
+ stroke: currentColor;
117
+ stroke-width: 2;
118
+ stroke-linecap: round;
119
+ stroke-linejoin: round;
120
+ }
121
+
122
+ .copy-button.copied {
123
+ color: var(--st-color-success);
124
+ }
125
+
126
+ pre {
127
+ margin: 0;
128
+ padding: var(--st-spacing-md);
129
+ overflow-x: auto;
130
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
131
+ font-size: 0.875rem;
132
+ line-height: 1.5;
133
+ color: var(--st-color-text);
134
+ }
135
+
136
+ .raw-content {
137
+ display: none;
138
+ }
139
+
140
+ /* ==========================================================
141
+ Prism.js Theme Mapping (Space Grey / Purple Aesthetic)
142
+ ========================================================== */
143
+ .token.comment, .token.prolog, .token.doctype, .token.cdata { color: var(--st-color-text-muted); font-style: italic; }
144
+ .token.punctuation { color: #8b949e; }
145
+ .token.namespace { opacity: .7; }
146
+ .token.property, .token.tag, .token.boolean, .token.number, .token.constant, .token.symbol, .token.deleted { color: #79c0ff; }
147
+ .token.selector, .token.attr-name, .token.string, .token.char, .token.builtin, .token.inserted { color: #a5d6ff; }
148
+ .token.operator, .token.entity, .token.url, .language-css .token.string, .style .token.string { color: var(--st-color-text); }
149
+ .token.atrule, .token.attr-value, .token.keyword { color: var(--st-color-primary); }
150
+ .token.function, .token.class-name { color: #d2a8ff; }
151
+ .token.regex, .token.important, .token.variable { color: #ffa657; }
152
+ `
153
+ ];
154
+
155
+ private _handleSlotChange() {
156
+ const nodes = this._slot.assignedNodes({ flatten: true });
157
+
158
+ // Find ALL template tags passed into the slot
159
+ const templates = nodes.filter(n => n instanceof HTMLTemplateElement) as HTMLTemplateElement[];
160
+
161
+ if (templates.length > 0) {
162
+ // Map over every template and parse its code and attributes
163
+ this._snippets = templates.map(template => {
164
+ const lang = template.getAttribute('data-lang') || this.language;
165
+ const label = template.getAttribute('data-label') || lang; // Fallback to lang if no label provided
166
+ const rawCode = this._dedent(template.innerHTML);
167
+
168
+ const grammar = Prism.languages[lang] || Prism.languages.javascript;
169
+ const highlightedCode = Prism.highlight(rawCode, grammar, lang);
170
+
171
+ return { lang, label, rawCode, highlightedCode };
172
+ });
173
+ } else {
174
+ // Fallback: If no template is used, process raw text just like before
175
+ const rawText = nodes.map(n => n.textContent).join('');
176
+ const rawCode = this._dedent(rawText);
177
+
178
+ if (rawCode) {
179
+ const grammar = Prism.languages[this.language] || Prism.languages.javascript;
180
+ const highlightedCode = Prism.highlight(rawCode, grammar, this.language);
181
+ this._snippets = [{ lang: this.language, label: this.language, rawCode, highlightedCode }];
182
+ }
183
+ }
184
+
185
+ // Reset to the first tab whenever new code is injected
186
+ this._activeIndex = 0;
187
+ }
188
+
189
+ private _dedent(text: string): string {
190
+ const lines = text.split('\n');
191
+ const minIndent = lines
192
+ .filter(line => line.trim().length > 0)
193
+ .reduce((min, line) => {
194
+ const indent = line.match(/^\s*/)?.[0].length || 0;
195
+ return Math.min(min, indent);
196
+ }, Infinity);
197
+
198
+ if (minIndent === Infinity || minIndent === 0) return text.trim();
199
+ return lines.map(line => line.slice(minIndent)).join('\n').trim();
200
+ }
201
+
202
+ private async _copyToClipboard() {
203
+ // Copy the code from the currently active tab
204
+ const activeSnippet = this._snippets[this._activeIndex];
205
+ if (!activeSnippet) return;
206
+
207
+ try {
208
+ await navigator.clipboard.writeText(activeSnippet.rawCode);
209
+ this._copied = true;
210
+ setTimeout(() => { this._copied = false; }, 2000);
211
+ } catch (err) {
212
+ console.error('Failed to copy text: ', err);
213
+ }
214
+ }
215
+
216
+ render() {
217
+ const activeSnippet = this._snippets[this._activeIndex];
218
+
219
+ return html`
220
+ <div class="header">
221
+ ${this._snippets.length > 1
222
+ ? html`
223
+ <div class="tabs">
224
+ ${this._snippets.map((snippet, index) => html`
225
+ <button
226
+ class="tab ${index === this._activeIndex ? 'active' : ''}"
227
+ @click=${() => this._activeIndex = index}
228
+ >
229
+ ${snippet.label}
230
+ </button>
231
+ `)}
232
+ </div>
233
+ `
234
+ : html`
235
+ <span class="language-label">${activeSnippet?.label || this.language}</span>
236
+ `
237
+ }
238
+
239
+ <button class="copy-button ${this._copied ? 'copied' : ''}" @click=${this._copyToClipboard}>
240
+ ${this._copied
241
+ ? html`<svg viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"></polyline></svg> Copied!`
242
+ : html`<svg viewBox="0 0 24 24"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> Copy`
243
+ }
244
+ </button>
245
+ </div>
246
+
247
+ <!-- Render the currently active snippet -->
248
+ ${activeSnippet ? html`
249
+ <pre><code class="language-${activeSnippet.lang}">${unsafeHTML(activeSnippet.highlightedCode)}</code></pre>
250
+ ` : ''}
251
+
252
+ <div class="raw-content">
253
+ <slot name="raw" @slotchange=${this._handleSlotChange}></slot>
254
+ </div>
255
+ `;
256
+ }
257
257
  }