create-nativecore 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/README.md +10 -18
  2. package/bin/index.mjs +407 -489
  3. package/package.json +4 -3
  4. package/template/.env.example +28 -0
  5. package/template/.htmlhintrc +14 -0
  6. package/template/api/data/dashboard.json +11 -0
  7. package/template/api/data/users.json +18 -0
  8. package/template/api/mockApi.js +161 -0
  9. package/template/assets/icon.svg +13 -0
  10. package/template/assets/logo.svg +25 -0
  11. package/template/eslint.config.js +94 -0
  12. package/template/index.html +137 -0
  13. package/template/manifest.json +19 -0
  14. package/template/public/.well-known/security.txt +9 -0
  15. package/template/public/_headers +24 -0
  16. package/template/public/_redirects +14 -0
  17. package/template/public/assets/icon.svg +13 -0
  18. package/template/public/assets/logo.svg +25 -0
  19. package/template/public/manifest.json +19 -0
  20. package/template/public/robots.txt +13 -0
  21. package/template/public/sitemap.xml +27 -0
  22. package/template/scripts/build-for-bots.mjs +121 -0
  23. package/template/scripts/convert-to-ts.mjs +106 -0
  24. package/template/scripts/fix-encoding.mjs +38 -0
  25. package/template/scripts/fix-svg-paths.mjs +32 -0
  26. package/template/scripts/generate-cf-router.mjs +52 -0
  27. package/template/scripts/inject-dev-tools.mjs +41 -0
  28. package/template/scripts/inject-version.mjs +65 -0
  29. package/template/scripts/make-component.mjs +445 -0
  30. package/template/scripts/make-component.mjs.backup +432 -0
  31. package/template/scripts/make-controller.mjs +119 -0
  32. package/template/scripts/make-core-component.mjs +303 -0
  33. package/template/scripts/make-view.mjs +346 -0
  34. package/template/scripts/minify.mjs +71 -0
  35. package/template/scripts/prepare-static-assets.mjs +141 -0
  36. package/template/scripts/prompt-bot-build.mjs +223 -0
  37. package/template/scripts/remove-component.mjs +170 -0
  38. package/template/scripts/remove-core-component.mjs +156 -0
  39. package/template/scripts/remove-dev.mjs +13 -0
  40. package/template/scripts/remove-view.mjs +200 -0
  41. package/template/scripts/strip-dev-blocks.mjs +30 -0
  42. package/template/scripts/watch-compile.mjs +69 -0
  43. package/template/server.js +1066 -0
  44. package/template/src/app.ts +115 -0
  45. package/template/src/components/appRegistry.ts +8 -0
  46. package/template/src/components/core/app-footer.ts +27 -0
  47. package/template/src/components/core/app-header.ts +175 -0
  48. package/template/src/components/core/app-sidebar.ts +238 -0
  49. package/template/src/components/core/loading-spinner.ts +25 -0
  50. package/template/src/components/core/nc-a.ts +313 -0
  51. package/template/src/components/core/nc-accordion.ts +186 -0
  52. package/template/src/components/core/nc-alert.ts +153 -0
  53. package/template/src/components/core/nc-animation.ts +1150 -0
  54. package/template/src/components/core/nc-autocomplete.ts +271 -0
  55. package/template/src/components/core/nc-avatar-group.ts +113 -0
  56. package/template/src/components/core/nc-avatar.ts +148 -0
  57. package/template/src/components/core/nc-badge.ts +86 -0
  58. package/template/src/components/core/nc-bottom-nav.ts +214 -0
  59. package/template/src/components/core/nc-breadcrumb.ts +96 -0
  60. package/template/src/components/core/nc-button.ts +307 -0
  61. package/template/src/components/core/nc-card.ts +160 -0
  62. package/template/src/components/core/nc-checkbox.ts +282 -0
  63. package/template/src/components/core/nc-chip.ts +115 -0
  64. package/template/src/components/core/nc-code.ts +314 -0
  65. package/template/src/components/core/nc-collapsible.ts +154 -0
  66. package/template/src/components/core/nc-color-picker.ts +268 -0
  67. package/template/src/components/core/nc-copy-button.ts +119 -0
  68. package/template/src/components/core/nc-date-picker.ts +443 -0
  69. package/template/src/components/core/nc-div.ts +280 -0
  70. package/template/src/components/core/nc-divider.ts +81 -0
  71. package/template/src/components/core/nc-drawer.ts +230 -0
  72. package/template/src/components/core/nc-dropdown.ts +178 -0
  73. package/template/src/components/core/nc-empty-state.ts +134 -0
  74. package/template/src/components/core/nc-file-upload.ts +354 -0
  75. package/template/src/components/core/nc-form.ts +312 -0
  76. package/template/src/components/core/nc-image.ts +184 -0
  77. package/template/src/components/core/nc-input.ts +383 -0
  78. package/template/src/components/core/nc-kbd.ts +48 -0
  79. package/template/src/components/core/nc-menu-item.ts +193 -0
  80. package/template/src/components/core/nc-menu.ts +376 -0
  81. package/template/src/components/core/nc-modal.ts +238 -0
  82. package/template/src/components/core/nc-nav-item.ts +151 -0
  83. package/template/src/components/core/nc-number-input.ts +350 -0
  84. package/template/src/components/core/nc-otp-input.ts +235 -0
  85. package/template/src/components/core/nc-pagination.ts +178 -0
  86. package/template/src/components/core/nc-popover.ts +260 -0
  87. package/template/src/components/core/nc-progress-circular.ts +119 -0
  88. package/template/src/components/core/nc-progress.ts +134 -0
  89. package/template/src/components/core/nc-radio.ts +235 -0
  90. package/template/src/components/core/nc-rating.ts +266 -0
  91. package/template/src/components/core/nc-rich-text.ts +283 -0
  92. package/template/src/components/core/nc-scroll-top.ts +116 -0
  93. package/template/src/components/core/nc-select.ts +452 -0
  94. package/template/src/components/core/nc-skeleton.ts +107 -0
  95. package/template/src/components/core/nc-slider.ts +285 -0
  96. package/template/src/components/core/nc-snackbar.ts +230 -0
  97. package/template/src/components/core/nc-splash.ts +343 -0
  98. package/template/src/components/core/nc-stepper.ts +247 -0
  99. package/template/src/components/core/nc-switch.ts +281 -0
  100. package/template/src/components/core/nc-tab-item.ts +138 -0
  101. package/template/src/components/core/nc-table.ts +279 -0
  102. package/template/src/components/core/nc-tabs.ts +554 -0
  103. package/template/src/components/core/nc-tag-input.ts +279 -0
  104. package/template/src/components/core/nc-textarea.ts +216 -0
  105. package/template/src/components/core/nc-time-picker.ts +438 -0
  106. package/template/src/components/core/nc-timeline.ts +186 -0
  107. package/template/src/components/core/nc-tooltip.ts +143 -0
  108. package/template/src/components/frameworkRegistry.ts +68 -0
  109. package/template/src/components/preloadRegistry.ts +28 -0
  110. package/template/src/components/registry.ts +8 -0
  111. package/template/src/components/ui/dashboard-signal-lab.ts +284 -0
  112. package/template/src/constants/apiEndpoints.ts +27 -0
  113. package/template/src/constants/errorMessages.ts +23 -0
  114. package/template/src/constants/index.ts +8 -0
  115. package/template/src/constants/routePaths.ts +15 -0
  116. package/template/src/constants/storageKeys.ts +18 -0
  117. package/template/src/controllers/dashboard.controller.ts +200 -0
  118. package/template/src/controllers/home.controller.ts +21 -0
  119. package/template/src/controllers/index.ts +11 -0
  120. package/template/src/controllers/login.controller.ts +131 -0
  121. package/template/src/core/component.ts +354 -0
  122. package/template/src/core/errorHandler.ts +85 -0
  123. package/template/src/core/gpu-animation.ts +604 -0
  124. package/template/src/core/http.ts +173 -0
  125. package/template/src/core/lazyComponents.ts +90 -0
  126. package/template/src/core/router.ts +642 -0
  127. package/template/src/core/signals.ts +146 -0
  128. package/template/src/core/state.ts +248 -0
  129. package/template/src/dev/component-editor.ts +1363 -0
  130. package/template/src/dev/component-overlay.ts +278 -0
  131. package/template/src/dev/context-menu.ts +223 -0
  132. package/template/src/dev/denc-tools.ts +250 -0
  133. package/template/src/dev/hmr.ts +189 -0
  134. package/template/src/dev/nfbs.code-workspace +27 -0
  135. package/template/src/dev/outline-panel.ts +1247 -0
  136. package/template/src/middleware/auth.middleware.ts +23 -0
  137. package/template/src/routes/routes.ts +38 -0
  138. package/template/src/services/api.service.ts +394 -0
  139. package/template/src/services/auth.service.ts +176 -0
  140. package/template/src/services/index.ts +8 -0
  141. package/template/src/services/logger.service.ts +74 -0
  142. package/template/src/services/storage.service.ts +88 -0
  143. package/template/src/stores/appStore.ts +57 -0
  144. package/template/src/stores/uiStore.ts +36 -0
  145. package/template/src/styles/core-variables.css +219 -0
  146. package/template/src/styles/core.css +710 -0
  147. package/template/src/styles/main.css +3164 -0
  148. package/template/src/styles/variables.css +152 -0
  149. package/template/src/types/global.d.ts +47 -0
  150. package/template/src/utils/cacheBuster.ts +20 -0
  151. package/template/src/utils/dom.ts +149 -0
  152. package/template/src/utils/events.ts +203 -0
  153. package/template/src/utils/form.ts +176 -0
  154. package/template/src/utils/formatters.ts +169 -0
  155. package/template/src/utils/helpers.ts +195 -0
  156. package/template/src/utils/markdown.ts +307 -0
  157. package/template/src/utils/sidebar.ts +96 -0
  158. package/template/src/utils/smoothScroll.ts +85 -0
  159. package/template/src/utils/templates.ts +23 -0
  160. package/template/src/utils/validation.ts +73 -0
  161. package/template/src/views/protected/dashboard.html +293 -0
  162. package/template/src/views/public/home.html +150 -0
  163. package/template/src/views/public/login.html +102 -0
  164. package/template/tests/unit/component.test.ts +87 -0
  165. package/template/tests/unit/computed.test.ts +79 -0
  166. package/template/tests/unit/form.test.ts +68 -0
  167. package/template/tests/unit/formatters.test.ts +49 -0
  168. package/template/tests/unit/lazy-components.test.ts +59 -0
  169. package/template/tests/unit/markdown.test.ts +62 -0
  170. package/template/tests/unit/router.test.ts +112 -0
  171. package/template/tests/unit/signals.test.ts +54 -0
  172. package/template/tests/unit/validation.test.ts +50 -0
  173. package/template/tsconfig.build.json +21 -0
  174. package/template/tsconfig.json +51 -0
  175. package/template/vitest.config.ts +36 -0
@@ -0,0 +1,268 @@
1
+ /**
2
+ * NcColorPicker Component
3
+ *
4
+ * Attributes:
5
+ * - name: string
6
+ * - value: string — current hex color (default: '#10b981')
7
+ * - swatches: JSON string array of hex colors — quick-pick palette
8
+ * - show-input: boolean — show hex text input (default: true)
9
+ * - disabled: boolean
10
+ * - size: 'sm'|'md'|'lg' (default: 'md')
11
+ *
12
+ * Events:
13
+ * - change: CustomEvent<{ value: string; name: string }>
14
+ * - input: CustomEvent<{ value: string; name: string }>
15
+ *
16
+ * Usage:
17
+ * <nc-color-picker name="bg" value="#3b82f6"></nc-color-picker>
18
+ * <nc-color-picker name="accent" swatches='["#ef4444","#f59e0b","#10b981","#3b82f6","#8b5cf6"]'></nc-color-picker>
19
+ */
20
+
21
+ import { Component, defineComponent } from '@core/component.js';
22
+
23
+ const DEFAULT_SWATCHES = [
24
+ '#ef4444','#f97316','#f59e0b','#eab308',
25
+ '#84cc16','#22c55e','#10b981','#14b8a6',
26
+ '#06b6d4','#3b82f6','#6366f1','#8b5cf6',
27
+ '#a855f7','#ec4899','#f43f5e','#64748b',
28
+ '#000000','#ffffff',
29
+ ];
30
+
31
+ export class NcColorPicker extends Component {
32
+ static useShadowDOM = true;
33
+
34
+ static get observedAttributes() {
35
+ return ['name', 'value', 'swatches', 'show-input', 'disabled', 'size'];
36
+ }
37
+
38
+ private _value = '#10b981';
39
+
40
+ constructor() { super(); }
41
+
42
+ private _getSwatches(): string[] {
43
+ const raw = this.getAttribute('swatches');
44
+ if (!raw) return DEFAULT_SWATCHES;
45
+ try { return JSON.parse(raw); } catch { return DEFAULT_SWATCHES; }
46
+ }
47
+
48
+ template() {
49
+ if (!this._mounted) {
50
+ this._value = this.getAttribute('value') || '#10b981';
51
+ }
52
+ const value = this._value;
53
+ const showInput = this.getAttribute('show-input') !== 'false';
54
+ const disabled = this.hasAttribute('disabled');
55
+ const swatches = this._getSwatches();
56
+
57
+ return `
58
+ <style>
59
+ :host { display: inline-block; font-family: var(--nc-font-family); }
60
+
61
+ .picker {
62
+ display: inline-flex;
63
+ flex-direction: column;
64
+ gap: var(--nc-spacing-sm);
65
+ opacity: ${disabled ? '0.5' : '1'};
66
+ pointer-events: ${disabled ? 'none' : 'auto'};
67
+ }
68
+
69
+ .swatches {
70
+ display: grid;
71
+ grid-template-columns: repeat(9, 1fr);
72
+ gap: 4px;
73
+ }
74
+
75
+ .swatch {
76
+ width: 24px;
77
+ height: 24px;
78
+ border-radius: 4px;
79
+ border: 2px solid transparent;
80
+ cursor: pointer;
81
+ transition: transform var(--nc-transition-fast), border-color var(--nc-transition-fast);
82
+ outline: none;
83
+ padding: 0;
84
+ }
85
+
86
+ :host([size="sm"]) .swatch { width: 18px; height: 18px; }
87
+ :host([size="lg"]) .swatch { width: 30px; height: 30px; }
88
+
89
+ .swatch:hover { transform: scale(1.15); }
90
+ .swatch:focus-visible { border-color: var(--nc-primary) !important; }
91
+ .swatch.active { border-color: var(--nc-primary) !important; transform: scale(1.1); }
92
+
93
+ /* White swatch needs a border to be visible */
94
+ .swatch--light { border-color: var(--nc-border) !important; }
95
+ .swatch--light.active { border-color: var(--nc-primary) !important; }
96
+
97
+ .input-row {
98
+ display: flex;
99
+ align-items: center;
100
+ gap: var(--nc-spacing-sm);
101
+ }
102
+
103
+ .preview {
104
+ width: 32px;
105
+ height: 32px;
106
+ border-radius: var(--nc-radius-sm, 4px);
107
+ border: 1px solid var(--nc-border);
108
+ flex-shrink: 0;
109
+ position: relative;
110
+ overflow: hidden;
111
+ cursor: pointer;
112
+ }
113
+
114
+ /* Native color input — hidden but triggered by clicking the preview */
115
+ .native-input {
116
+ position: absolute;
117
+ inset: 0;
118
+ opacity: 0;
119
+ cursor: pointer;
120
+ width: 100%;
121
+ height: 100%;
122
+ padding: 0;
123
+ border: none;
124
+ }
125
+
126
+ .hex-input {
127
+ flex: 1;
128
+ padding: 4px 8px;
129
+ background: var(--nc-bg);
130
+ border: var(--nc-input-border);
131
+ border-radius: var(--nc-input-radius);
132
+ color: var(--nc-text);
133
+ font-size: var(--nc-font-size-sm);
134
+ font-family: var(--nc-font-family);
135
+ font-variant-numeric: tabular-nums;
136
+ outline: none;
137
+ width: 90px;
138
+ }
139
+
140
+ .hex-input:focus { border-color: var(--nc-input-focus-border); box-shadow: 0 0 0 2px rgba(16,185,129,.15); }
141
+ </style>
142
+ <div class="picker">
143
+ <div class="swatches" role="listbox" aria-label="Color swatches">
144
+ ${swatches.map(color => {
145
+ const isLight = this._isLight(color);
146
+ const active = color.toLowerCase() === value.toLowerCase();
147
+ return `<button
148
+ class="swatch${active ? ' active' : ''}${isLight ? ' swatch--light' : ''}"
149
+ style="background:${color}"
150
+ data-color="${color}"
151
+ aria-label="${color}"
152
+ aria-selected="${active}"
153
+ role="option"
154
+ type="button"
155
+ title="${color}"
156
+ ></button>`;
157
+ }).join('')}
158
+ </div>
159
+
160
+ ${showInput ? `
161
+ <div class="input-row">
162
+ <div class="preview" style="background:${value}" title="Click to open color picker">
163
+ <input class="native-input" type="color" value="${value}" aria-label="Color picker" />
164
+ </div>
165
+ <input class="hex-input" type="text" value="${value}" maxlength="7" placeholder="#rrggbb" aria-label="Hex color value" />
166
+ </div>` : ''}
167
+
168
+ <input type="hidden" name="${this.getAttribute('name') || ''}" value="${value}" />
169
+ </div>
170
+ `;
171
+ }
172
+
173
+ private _isLight(hex: string): boolean {
174
+ const c = hex.replace('#', '');
175
+ const r = parseInt(c.slice(0, 2), 16);
176
+ const g = parseInt(c.slice(2, 4), 16);
177
+ const b = parseInt(c.slice(4, 6), 16);
178
+ return (r * 299 + g * 587 + b * 114) / 1000 > 200;
179
+ }
180
+
181
+ onMount() {
182
+ this._bindEvents();
183
+ }
184
+
185
+ private _bindEvents() {
186
+ // Swatch clicks
187
+ this.$<HTMLElement>('.swatches')!.addEventListener('click', (e) => {
188
+ const btn = (e.target as HTMLElement).closest<HTMLElement>('[data-color]');
189
+ if (btn) this._commit(btn.dataset.color!);
190
+ });
191
+
192
+ // Native color input
193
+ const native = this.$<HTMLInputElement>('.native-input');
194
+ if (native) {
195
+ native.addEventListener('input', () => {
196
+ this._commit(native.value, false);
197
+ const hex = this.$<HTMLInputElement>('.hex-input');
198
+ if (hex) hex.value = native.value;
199
+ });
200
+ native.addEventListener('change', () => this._commit(native.value));
201
+ }
202
+
203
+ // Hex text input
204
+ const hexIn = this.$<HTMLInputElement>('.hex-input');
205
+ if (hexIn) {
206
+ hexIn.addEventListener('input', () => {
207
+ const v = hexIn.value.trim();
208
+ if (/^#[0-9a-fA-F]{6}$/.test(v)) {
209
+ this._commit(v, false);
210
+ const nat = this.$<HTMLInputElement>('.native-input');
211
+ if (nat) nat.value = v;
212
+ }
213
+ });
214
+ hexIn.addEventListener('change', () => {
215
+ const v = hexIn.value.trim();
216
+ if (/^#[0-9a-fA-F]{6}$/.test(v)) this._commit(v);
217
+ else hexIn.value = this._value; // revert invalid
218
+ });
219
+ }
220
+ }
221
+
222
+ private _commit(value: string, fireChange = true) {
223
+ this._value = value;
224
+ this.setAttribute('value', value);
225
+
226
+ // Update swatch active states
227
+ this.$$<HTMLElement>('.swatch').forEach(s => {
228
+ const active = s.dataset.color?.toLowerCase() === value.toLowerCase();
229
+ s.classList.toggle('active', active);
230
+ s.setAttribute('aria-selected', String(active));
231
+ });
232
+
233
+ // Update preview
234
+ const preview = this.$<HTMLElement>('.preview');
235
+ if (preview) preview.style.background = value;
236
+
237
+ // Update hidden input
238
+ const hidden = this.$<HTMLInputElement>('input[type="hidden"]');
239
+ if (hidden) hidden.value = value;
240
+
241
+ const name = this.getAttribute('name') || '';
242
+ const eventType = fireChange ? 'change' : 'input';
243
+ this.dispatchEvent(new CustomEvent(eventType, {
244
+ bubbles: true, composed: true,
245
+ detail: { value, name }
246
+ }));
247
+ if (fireChange) {
248
+ this.dispatchEvent(new CustomEvent('input', {
249
+ bubbles: true, composed: true,
250
+ detail: { value, name }
251
+ }));
252
+ }
253
+ }
254
+
255
+ attributeChangedCallback(name: string, oldValue: string, newValue: string) {
256
+ if (oldValue === newValue) return;
257
+ if (name === 'value' && this._mounted) {
258
+ this._value = newValue;
259
+ this._commit(newValue, false);
260
+ const hex = this.$<HTMLInputElement>('.hex-input');
261
+ if (hex) hex.value = newValue;
262
+ return;
263
+ }
264
+ if (this._mounted) { this.render(); this._bindEvents(); }
265
+ }
266
+ }
267
+
268
+ defineComponent('nc-color-picker', NcColorPicker);
@@ -0,0 +1,119 @@
1
+ /**
2
+ * NcCopyButton Component — copy text to clipboard with auto feedback
3
+ *
4
+ * Attributes:
5
+ * value — text to copy (required)
6
+ * label — idle button label (default: 'Copy')
7
+ * copied-label — label after copy (default: 'Copied!')
8
+ * timeout — ms before reverting label (default: 2000)
9
+ * variant — button variant passed to inner button (default: 'outline')
10
+ * size — 'sm'|'md'|'lg' (default: 'md')
11
+ * icon-only — boolean — show icon only, no text
12
+ *
13
+ * Events:
14
+ * copy — CustomEvent<{ value: string }> — fires on successful copy
15
+ * error — CustomEvent<{ error: unknown }> — fires if clipboard write fails
16
+ */
17
+ import { Component, defineComponent } from '@core/component.js';
18
+
19
+ export class NcCopyButton extends Component {
20
+ static useShadowDOM = true;
21
+
22
+ private _timer: ReturnType<typeof setTimeout> | null = null;
23
+ private _copied = false;
24
+
25
+ template() {
26
+ const label = this.getAttribute('label') ?? 'Copy';
27
+ const copiedLabel = this.getAttribute('copied-label') ?? 'Copied!';
28
+ const variant = this.getAttribute('variant') ?? 'outline';
29
+ const size = this.getAttribute('size') ?? 'md';
30
+ const iconOnly = this.hasAttribute('icon-only');
31
+ const isCopied = this._copied;
32
+
33
+ const pad = size === 'sm' ? '4px 8px' : size === 'lg' ? '8px 20px' : '6px 14px';
34
+ const fs = size === 'sm' ? 'var(--nc-font-size-xs)' : size === 'lg' ? 'var(--nc-font-size-base)' : 'var(--nc-font-size-sm)';
35
+ const iconSz = size === 'sm' ? 14 : size === 'lg' ? 20 : 16;
36
+
37
+ const copyIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="${iconSz}" height="${iconSz}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
38
+ <rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
39
+ </svg>`;
40
+ const checkIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="${iconSz}" height="${iconSz}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
41
+ <polyline points="20 6 9 17 4 12"/>
42
+ </svg>`;
43
+
44
+ const variantColors: Record<string, string> = {
45
+ outline: 'var(--nc-border)',
46
+ primary: 'var(--nc-primary)',
47
+ ghost: 'transparent',
48
+ };
49
+ const border = variantColors[variant] ?? variantColors.outline;
50
+ const bg = variant === 'primary' ? 'var(--nc-primary)' : 'transparent';
51
+ const color = isCopied
52
+ ? 'var(--nc-success)'
53
+ : variant === 'primary' ? 'var(--nc-white)' : 'var(--nc-text)';
54
+
55
+ return `
56
+ <style>
57
+ :host { display: inline-block; }
58
+ button {
59
+ display: inline-flex;
60
+ align-items: center;
61
+ gap: 6px;
62
+ font-family: var(--nc-font-family);
63
+ font-size: ${fs};
64
+ font-weight: var(--nc-font-weight-medium);
65
+ color: ${color};
66
+ background: ${bg};
67
+ border: 1px solid ${border};
68
+ border-radius: var(--nc-radius-md);
69
+ padding: ${pad};
70
+ cursor: pointer;
71
+ transition: color var(--nc-transition-fast), background var(--nc-transition-fast),
72
+ border-color var(--nc-transition-fast), opacity var(--nc-transition-fast);
73
+ outline: none;
74
+ white-space: nowrap;
75
+ }
76
+ button:hover:not(:disabled) { opacity: 0.8; }
77
+ button:focus-visible { outline: 2px solid var(--nc-primary); outline-offset: 2px; }
78
+ button:disabled { opacity: 0.5; cursor: default; }
79
+ .icon { flex-shrink: 0; display: flex; }
80
+ </style>
81
+ <button type="button" aria-label="${isCopied ? copiedLabel : label}" ${isCopied ? 'disabled' : ''}>
82
+ <span class="icon">${isCopied ? checkIcon : copyIcon}</span>
83
+ ${!iconOnly ? `<span>${isCopied ? copiedLabel : label}</span>` : ''}
84
+ </button>
85
+ `;
86
+ }
87
+
88
+ onMount() {
89
+ this.shadowRoot!.addEventListener('click', () => this._copy());
90
+ }
91
+
92
+ private async _copy() {
93
+ const value = this.getAttribute('value') ?? '';
94
+ const timeout = parseInt(this.getAttribute('timeout') ?? '2000', 10);
95
+ try {
96
+ await navigator.clipboard.writeText(value);
97
+ this._copied = true;
98
+ this.render();
99
+ if (this._timer) clearTimeout(this._timer);
100
+ this._timer = setTimeout(() => {
101
+ this._copied = false;
102
+ this.render();
103
+ }, timeout);
104
+ this.dispatchEvent(new CustomEvent('copy', {
105
+ detail: { value }, bubbles: true, composed: true,
106
+ }));
107
+ } catch (error) {
108
+ this.dispatchEvent(new CustomEvent('error', {
109
+ detail: { error }, bubbles: true, composed: true,
110
+ }));
111
+ }
112
+ }
113
+
114
+ onUnmount() {
115
+ if (this._timer) clearTimeout(this._timer);
116
+ }
117
+ }
118
+
119
+ defineComponent('nc-copy-button', NcCopyButton);