nativecorejs 0.1.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.
- package/README.md +22 -0
- package/dist/components/builtinRegistry.d.ts +2 -0
- package/dist/components/builtinRegistry.js +72 -0
- package/dist/components/index.d.ts +59 -0
- package/dist/components/index.js +59 -0
- package/dist/components/loading-spinner.d.ts +5 -0
- package/dist/components/loading-spinner.js +48 -0
- package/dist/components/nc-a.d.ts +45 -0
- package/dist/components/nc-a.js +290 -0
- package/dist/components/nc-accordion.d.ts +36 -0
- package/dist/components/nc-accordion.js +186 -0
- package/dist/components/nc-alert.d.ts +11 -0
- package/dist/components/nc-alert.js +127 -0
- package/dist/components/nc-animation.d.ts +117 -0
- package/dist/components/nc-animation.js +1053 -0
- package/dist/components/nc-autocomplete.d.ts +41 -0
- package/dist/components/nc-autocomplete.js +275 -0
- package/dist/components/nc-avatar-group.d.ts +7 -0
- package/dist/components/nc-avatar-group.js +85 -0
- package/dist/components/nc-avatar.d.ts +9 -0
- package/dist/components/nc-avatar.js +127 -0
- package/dist/components/nc-badge.d.ts +7 -0
- package/dist/components/nc-badge.js +63 -0
- package/dist/components/nc-bottom-nav.d.ts +53 -0
- package/dist/components/nc-bottom-nav.js +198 -0
- package/dist/components/nc-breadcrumb.d.ts +10 -0
- package/dist/components/nc-breadcrumb.js +71 -0
- package/dist/components/nc-button.d.ts +38 -0
- package/dist/components/nc-button.js +293 -0
- package/dist/components/nc-card.d.ts +11 -0
- package/dist/components/nc-card.js +74 -0
- package/dist/components/nc-checkbox.d.ts +16 -0
- package/dist/components/nc-checkbox.js +194 -0
- package/dist/components/nc-chip.d.ts +8 -0
- package/dist/components/nc-chip.js +89 -0
- package/dist/components/nc-code.d.ts +37 -0
- package/dist/components/nc-code.js +315 -0
- package/dist/components/nc-collapsible.d.ts +33 -0
- package/dist/components/nc-collapsible.js +148 -0
- package/dist/components/nc-color-picker.d.ts +33 -0
- package/dist/components/nc-color-picker.js +265 -0
- package/dist/components/nc-copy-button.d.ts +10 -0
- package/dist/components/nc-copy-button.js +94 -0
- package/dist/components/nc-date-picker.d.ts +41 -0
- package/dist/components/nc-date-picker.js +443 -0
- package/dist/components/nc-div.d.ts +53 -0
- package/dist/components/nc-div.js +270 -0
- package/dist/components/nc-divider.d.ts +7 -0
- package/dist/components/nc-divider.js +57 -0
- package/dist/components/nc-drawer.d.ts +40 -0
- package/dist/components/nc-drawer.js +217 -0
- package/dist/components/nc-dropdown.d.ts +41 -0
- package/dist/components/nc-dropdown.js +170 -0
- package/dist/components/nc-empty-state.d.ts +5 -0
- package/dist/components/nc-empty-state.js +76 -0
- package/dist/components/nc-file-upload.d.ts +40 -0
- package/dist/components/nc-file-upload.js +336 -0
- package/dist/components/nc-form.d.ts +70 -0
- package/dist/components/nc-form.js +273 -0
- package/dist/components/nc-image.d.ts +10 -0
- package/dist/components/nc-image.js +139 -0
- package/dist/components/nc-input.d.ts +25 -0
- package/dist/components/nc-input.js +302 -0
- package/dist/components/nc-kbd.d.ts +5 -0
- package/dist/components/nc-kbd.js +34 -0
- package/dist/components/nc-menu-item.d.ts +43 -0
- package/dist/components/nc-menu-item.js +182 -0
- package/dist/components/nc-menu.d.ts +76 -0
- package/dist/components/nc-menu.js +360 -0
- package/dist/components/nc-modal.d.ts +51 -0
- package/dist/components/nc-modal.js +231 -0
- package/dist/components/nc-nav-item.d.ts +35 -0
- package/dist/components/nc-nav-item.js +142 -0
- package/dist/components/nc-number-input.d.ts +22 -0
- package/dist/components/nc-number-input.js +270 -0
- package/dist/components/nc-otp-input.d.ts +41 -0
- package/dist/components/nc-otp-input.js +227 -0
- package/dist/components/nc-pagination.d.ts +28 -0
- package/dist/components/nc-pagination.js +171 -0
- package/dist/components/nc-popover.d.ts +58 -0
- package/dist/components/nc-popover.js +301 -0
- package/dist/components/nc-progress-circular.d.ts +7 -0
- package/dist/components/nc-progress-circular.js +67 -0
- package/dist/components/nc-progress.d.ts +7 -0
- package/dist/components/nc-progress.js +109 -0
- package/dist/components/nc-radio.d.ts +13 -0
- package/dist/components/nc-radio.js +169 -0
- package/dist/components/nc-rating.d.ts +19 -0
- package/dist/components/nc-rating.js +187 -0
- package/dist/components/nc-rich-text.d.ts +43 -0
- package/dist/components/nc-rich-text.js +310 -0
- package/dist/components/nc-scroll-top.d.ts +28 -0
- package/dist/components/nc-scroll-top.js +103 -0
- package/dist/components/nc-select.d.ts +51 -0
- package/dist/components/nc-select.js +425 -0
- package/dist/components/nc-skeleton.d.ts +7 -0
- package/dist/components/nc-skeleton.js +90 -0
- package/dist/components/nc-slider.d.ts +41 -0
- package/dist/components/nc-slider.js +268 -0
- package/dist/components/nc-snackbar.d.ts +51 -0
- package/dist/components/nc-snackbar.js +200 -0
- package/dist/components/nc-splash.d.ts +25 -0
- package/dist/components/nc-splash.js +296 -0
- package/dist/components/nc-stepper.d.ts +50 -0
- package/dist/components/nc-stepper.js +236 -0
- package/dist/components/nc-switch.d.ts +14 -0
- package/dist/components/nc-switch.js +194 -0
- package/dist/components/nc-tab-item.d.ts +39 -0
- package/dist/components/nc-tab-item.js +127 -0
- package/dist/components/nc-table.d.ts +44 -0
- package/dist/components/nc-table.js +265 -0
- package/dist/components/nc-tabs.d.ts +79 -0
- package/dist/components/nc-tabs.js +519 -0
- package/dist/components/nc-tag-input.d.ts +49 -0
- package/dist/components/nc-tag-input.js +268 -0
- package/dist/components/nc-textarea.d.ts +15 -0
- package/dist/components/nc-textarea.js +164 -0
- package/dist/components/nc-time-picker.d.ts +51 -0
- package/dist/components/nc-time-picker.js +452 -0
- package/dist/components/nc-timeline.d.ts +53 -0
- package/dist/components/nc-timeline.js +171 -0
- package/dist/components/nc-tooltip.d.ts +27 -0
- package/dist/components/nc-tooltip.js +135 -0
- package/dist/core/component.d.ts +33 -0
- package/dist/core/component.js +208 -0
- package/dist/core/gpu-animation.d.ts +141 -0
- package/dist/core/gpu-animation.js +474 -0
- package/dist/core/lazyComponents.d.ts +13 -0
- package/dist/core/lazyComponents.js +73 -0
- package/dist/core/router.d.ts +55 -0
- package/dist/core/router.js +424 -0
- package/dist/core/state.d.ts +18 -0
- package/dist/core/state.js +153 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +11 -0
- package/dist/utils/cacheBuster.d.ts +9 -0
- package/dist/utils/cacheBuster.js +12 -0
- package/dist/utils/dom.d.ts +16 -0
- package/dist/utils/dom.js +70 -0
- package/dist/utils/events.d.ts +20 -0
- package/dist/utils/events.js +80 -0
- package/dist/utils/templates.d.ts +2 -0
- package/dist/utils/templates.js +2 -0
- package/package.json +53 -0
- package/src/styles/base.css +40 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nc-splash Component
|
|
3
|
+
*
|
|
4
|
+
* Standalone full-screen splash screen with dissolve effect.
|
|
5
|
+
* Shows branding then dissolves into particles.
|
|
6
|
+
* Use anywhere without needing to wrap content.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* </nc-splash>
|
|
10
|
+
*
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
import { Component, defineComponent } from '../core/component.js';
|
|
14
|
+
import { useState } from '../core/state.js';
|
|
15
|
+
import { createAnimationLoop } from '../core/gpu-animation.js';
|
|
16
|
+
export class NcSplash extends Component {
|
|
17
|
+
static useShadowDOM = true;
|
|
18
|
+
isComplete;
|
|
19
|
+
canvas;
|
|
20
|
+
animationLoop;
|
|
21
|
+
static get observedAttributes() {
|
|
22
|
+
return ['particles', 'duration', 'delay', 'title', 'subtitle'];
|
|
23
|
+
}
|
|
24
|
+
constructor() {
|
|
25
|
+
super();
|
|
26
|
+
this.isComplete = useState(false);
|
|
27
|
+
}
|
|
28
|
+
template() {
|
|
29
|
+
const title = this.attr('title', 'NativeCore');
|
|
30
|
+
const subtitle = this.attr('subtitle', '');
|
|
31
|
+
return `
|
|
32
|
+
<style>
|
|
33
|
+
:host {
|
|
34
|
+
display: block;
|
|
35
|
+
position: fixed;
|
|
36
|
+
inset: 0;
|
|
37
|
+
width: auto;
|
|
38
|
+
height: auto;
|
|
39
|
+
z-index: 99999;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.splash-overlay {
|
|
43
|
+
position: absolute;
|
|
44
|
+
top: 0;
|
|
45
|
+
left: 0;
|
|
46
|
+
width: 100%;
|
|
47
|
+
height: 100%;
|
|
48
|
+
background: linear-gradient(135deg, rgba(15, 23, 42, 0.95) 0%, rgba(30, 41, 59, 0.98) 100%);
|
|
49
|
+
backdrop-filter: blur(20px);
|
|
50
|
+
-webkit-backdrop-filter: blur(20px);
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
align-items: center;
|
|
54
|
+
justify-content: center;
|
|
55
|
+
transition: opacity 0.8s cubic-bezier(0.4, 0, 0.2, 1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.splash-overlay.fade-out {
|
|
59
|
+
opacity: 0;
|
|
60
|
+
pointer-events: none;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.splash-overlay.hidden {
|
|
64
|
+
display: none;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.splash-content {
|
|
68
|
+
text-align: center;
|
|
69
|
+
z-index: 1;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.splash-prompt {
|
|
73
|
+
margin-top: 2.5rem;
|
|
74
|
+
font-size: 1.25rem;
|
|
75
|
+
color: #fff;
|
|
76
|
+
opacity: 0.85;
|
|
77
|
+
letter-spacing: 0.1em;
|
|
78
|
+
font-family: 'Fira Mono', 'Fira Code', monospace;
|
|
79
|
+
display: flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
justify-content: center;
|
|
82
|
+
gap: 0.5em;
|
|
83
|
+
user-select: none;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.splash-cursor {
|
|
87
|
+
display: inline-block;
|
|
88
|
+
width: 1ch;
|
|
89
|
+
height: 1.2em;
|
|
90
|
+
background: none;
|
|
91
|
+
border-right: 2px solid #fff;
|
|
92
|
+
animation: blink-cursor 1s steps(1) infinite;
|
|
93
|
+
margin-left: 0.1em;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@keyframes blink-cursor {
|
|
97
|
+
0%, 49% { opacity: 1; }
|
|
98
|
+
50%, 100% { opacity: 0; }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.splash-title {
|
|
102
|
+
font-size: clamp(3rem, 12vw, 8rem);
|
|
103
|
+
font-weight: 800;
|
|
104
|
+
background: linear-gradient(135deg, #10b981 0%, #3b82f6 50%, #06b6d4 100%);
|
|
105
|
+
-webkit-background-clip: text;
|
|
106
|
+
-webkit-text-fill-color: transparent;
|
|
107
|
+
background-clip: text;
|
|
108
|
+
letter-spacing: -0.02em;
|
|
109
|
+
margin: 0;
|
|
110
|
+
text-shadow: 0 0 80px rgba(16, 185, 129, 0.5);
|
|
111
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.splash-subtitle {
|
|
115
|
+
font-size: clamp(0.875rem, 2vw, 1.25rem);
|
|
116
|
+
color: rgba(255, 255, 255, 0.6);
|
|
117
|
+
margin-top: 1rem;
|
|
118
|
+
font-weight: 400;
|
|
119
|
+
letter-spacing: 0.2em;
|
|
120
|
+
text-transform: uppercase;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.splash-glow {
|
|
124
|
+
position: absolute;
|
|
125
|
+
width: 600px;
|
|
126
|
+
height: 600px;
|
|
127
|
+
background: radial-gradient(circle, rgba(16, 185, 129, 0.15) 0%, transparent 70%);
|
|
128
|
+
pointer-events: none;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.splash-canvas {
|
|
132
|
+
position: absolute;
|
|
133
|
+
top: 0;
|
|
134
|
+
left: 0;
|
|
135
|
+
width: 100%;
|
|
136
|
+
height: 100%;
|
|
137
|
+
pointer-events: none;
|
|
138
|
+
z-index: 100000;
|
|
139
|
+
}
|
|
140
|
+
</style>
|
|
141
|
+
|
|
142
|
+
<div class="splash-overlay" id="overlay">
|
|
143
|
+
<div class="splash-glow"></div>
|
|
144
|
+
<div class="splash-content" id="content">
|
|
145
|
+
<h1 class="splash-title">${title}</h1>
|
|
146
|
+
${subtitle ? `<p class="splash-subtitle">${subtitle}</p>` : ''}
|
|
147
|
+
<div class="splash-prompt" id="prompt">
|
|
148
|
+
Click to start <span class="splash-cursor"></span>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
`;
|
|
153
|
+
}
|
|
154
|
+
onMount() {
|
|
155
|
+
// Check if splash has already been shown this session
|
|
156
|
+
const splashShown = sessionStorage.getItem('splash-shown');
|
|
157
|
+
const connection = navigator.connection;
|
|
158
|
+
const shouldSkipSplash = window.matchMedia('(max-width: 768px)').matches ||
|
|
159
|
+
window.matchMedia('(prefers-reduced-motion: reduce)').matches ||
|
|
160
|
+
connection?.saveData === true;
|
|
161
|
+
if (splashShown === 'true' || shouldSkipSplash) {
|
|
162
|
+
// Skip splash, hide component but keep in DOM for dev tools
|
|
163
|
+
this.style.display = 'none';
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const overlay = this.$('#overlay');
|
|
167
|
+
const prompt = this.$('#prompt');
|
|
168
|
+
if (overlay && prompt) {
|
|
169
|
+
const clickHandler = () => {
|
|
170
|
+
prompt.style.opacity = '0.5';
|
|
171
|
+
prompt.style.pointerEvents = 'none';
|
|
172
|
+
setTimeout(() => {
|
|
173
|
+
prompt.style.display = 'none';
|
|
174
|
+
}, 300);
|
|
175
|
+
this.startDissolve();
|
|
176
|
+
overlay.removeEventListener('click', clickHandler);
|
|
177
|
+
window.removeEventListener('keydown', keyHandler);
|
|
178
|
+
};
|
|
179
|
+
const keyHandler = (e) => {
|
|
180
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
181
|
+
clickHandler();
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
overlay.addEventListener('click', clickHandler);
|
|
185
|
+
window.addEventListener('keydown', keyHandler);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
startDissolve() {
|
|
189
|
+
const overlay = this.$('#overlay');
|
|
190
|
+
const content = this.$('#content');
|
|
191
|
+
if (!overlay || !content)
|
|
192
|
+
return;
|
|
193
|
+
const particleCount = parseInt(this.attr('particles', '10000') ?? '10000', 10);
|
|
194
|
+
const duration = parseInt(this.attr('duration', '2500') ?? '2500', 10);
|
|
195
|
+
// Get content bounding rect
|
|
196
|
+
const rect = content.getBoundingClientRect();
|
|
197
|
+
// Sample colors from the gradient (emerald green palette)
|
|
198
|
+
const colors = [
|
|
199
|
+
'#10b981', '#059669', '#34d399', '#14b8a6',
|
|
200
|
+
'#3b82f6', '#2563eb', '#06b6d4', '#0891b2'
|
|
201
|
+
];
|
|
202
|
+
// Create canvas
|
|
203
|
+
const dpr = window.devicePixelRatio || 1;
|
|
204
|
+
this.canvas = document.createElement('canvas');
|
|
205
|
+
this.canvas.className = 'splash-canvas';
|
|
206
|
+
this.canvas.width = window.innerWidth * dpr;
|
|
207
|
+
this.canvas.height = window.innerHeight * dpr;
|
|
208
|
+
this.canvas.style.cssText = `position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:100000`;
|
|
209
|
+
const ctx = this.canvas.getContext('2d', { alpha: true });
|
|
210
|
+
if (!ctx)
|
|
211
|
+
return;
|
|
212
|
+
ctx.scale(dpr, dpr);
|
|
213
|
+
// Create particles in a grid covering the content area
|
|
214
|
+
const particles = [];
|
|
215
|
+
const gridSize = Math.ceil(Math.sqrt(particleCount));
|
|
216
|
+
const cellWidth = rect.width / gridSize;
|
|
217
|
+
const cellHeight = rect.height / gridSize;
|
|
218
|
+
for (let i = 0; i < particleCount; i++) {
|
|
219
|
+
const row = Math.floor(i / gridSize);
|
|
220
|
+
const col = i % gridSize;
|
|
221
|
+
const angle = Math.random() * Math.PI * 2;
|
|
222
|
+
const speed = 1 + Math.random() * 4;
|
|
223
|
+
particles.push({
|
|
224
|
+
x: rect.left + col * cellWidth + cellWidth / 2 + (Math.random() - 0.5) * cellWidth * 0.5,
|
|
225
|
+
y: rect.top + row * cellHeight + cellHeight / 2 + (Math.random() - 0.5) * cellHeight * 0.5,
|
|
226
|
+
vx: Math.cos(angle) * speed,
|
|
227
|
+
vy: Math.sin(angle) * speed - 0.5, // slight upward bias
|
|
228
|
+
size: Math.max(cellWidth, cellHeight) * (0.6 + Math.random() * 0.4),
|
|
229
|
+
color: colors[Math.floor(Math.random() * colors.length)],
|
|
230
|
+
alpha: 0.8 + Math.random() * 0.2,
|
|
231
|
+
rotation: Math.random() * Math.PI * 2,
|
|
232
|
+
rotationSpeed: (Math.random() - 0.5) * 0.15
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
// Hide original content, show canvas
|
|
236
|
+
content.style.opacity = '0';
|
|
237
|
+
this.shadowRoot?.appendChild(this.canvas);
|
|
238
|
+
const startTime = performance.now();
|
|
239
|
+
const fadeStart = duration * 0.4;
|
|
240
|
+
this.animationLoop = createAnimationLoop((dt) => {
|
|
241
|
+
const elapsed = performance.now() - startTime;
|
|
242
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
243
|
+
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
|
|
244
|
+
for (const p of particles) {
|
|
245
|
+
// Physics
|
|
246
|
+
p.x += p.vx * dt * 60;
|
|
247
|
+
p.y += p.vy * dt * 60;
|
|
248
|
+
p.vy += 0.08 * dt * 60; // gravity
|
|
249
|
+
p.rotation += p.rotationSpeed;
|
|
250
|
+
// Fade out
|
|
251
|
+
if (elapsed > fadeStart) {
|
|
252
|
+
p.alpha = Math.max(0, (1 - ((elapsed - fadeStart) / (duration - fadeStart))) * 0.9);
|
|
253
|
+
}
|
|
254
|
+
// Draw
|
|
255
|
+
if (p.alpha > 0.01) {
|
|
256
|
+
ctx.save();
|
|
257
|
+
ctx.translate(p.x, p.y);
|
|
258
|
+
ctx.rotate(p.rotation);
|
|
259
|
+
ctx.globalAlpha = p.alpha;
|
|
260
|
+
ctx.fillStyle = p.color;
|
|
261
|
+
ctx.fillRect(-p.size / 2, -p.size / 2, p.size, p.size);
|
|
262
|
+
ctx.restore();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (progress >= 1) {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
return true;
|
|
269
|
+
});
|
|
270
|
+
this.animationLoop.start();
|
|
271
|
+
// Start fading overlay
|
|
272
|
+
setTimeout(() => {
|
|
273
|
+
overlay.classList.add('fade-out');
|
|
274
|
+
}, duration * 0.6);
|
|
275
|
+
// Complete cleanup - mark as shown and hide component
|
|
276
|
+
setTimeout(() => {
|
|
277
|
+
this.animationLoop?.stop();
|
|
278
|
+
this.canvas?.remove();
|
|
279
|
+
overlay.classList.add('hidden');
|
|
280
|
+
this.isComplete.value = true;
|
|
281
|
+
// Mark splash as shown in sessionStorage
|
|
282
|
+
sessionStorage.setItem('splash-shown', 'true');
|
|
283
|
+
this.dispatchEvent(new CustomEvent('splash-complete', {
|
|
284
|
+
bubbles: true,
|
|
285
|
+
composed: true
|
|
286
|
+
}));
|
|
287
|
+
// Hide the component but keep in DOM for dev tools
|
|
288
|
+
setTimeout(() => this.style.display = 'none', 100);
|
|
289
|
+
}, duration + 800);
|
|
290
|
+
}
|
|
291
|
+
disconnectedCallback() {
|
|
292
|
+
this.animationLoop?.stop();
|
|
293
|
+
this.canvas?.remove();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
defineComponent('nc-splash', NcSplash);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NcStepper + NcStep Components
|
|
3
|
+
*
|
|
4
|
+
* Multi-step wizard with numbered step indicators.
|
|
5
|
+
*
|
|
6
|
+
* nc-stepper:
|
|
7
|
+
* - step: number - current step index (0-based, default: 0)
|
|
8
|
+
* - orientation: 'horizontal'|'vertical' (default: 'horizontal')
|
|
9
|
+
* - variant: 'default'|'simple' (default: 'default')
|
|
10
|
+
* - linear: boolean - prevent skipping ahead (default: false)
|
|
11
|
+
*
|
|
12
|
+
* nc-step:
|
|
13
|
+
* - label: string - step label
|
|
14
|
+
* - description: string - optional sub-label
|
|
15
|
+
* - status: 'complete'|'error'|'' - force a status icon (auto-set by stepper normally)
|
|
16
|
+
*
|
|
17
|
+
* Events (on nc-stepper):
|
|
18
|
+
* - change: CustomEvent<{ step: number; prev: number }>
|
|
19
|
+
*
|
|
20
|
+
* Methods (call on the element):
|
|
21
|
+
* stepper.next()
|
|
22
|
+
* stepper.prev()
|
|
23
|
+
* stepper.goTo(index)
|
|
24
|
+
*
|
|
25
|
+
* Usage:
|
|
26
|
+
* <nc-stepper id="wizard" step="0">
|
|
27
|
+
* <nc-step label="Account" description="Basic info"></nc-step>
|
|
28
|
+
* <nc-step label="Profile"></nc-step>
|
|
29
|
+
* <nc-step label="Review"></nc-step>
|
|
30
|
+
* </nc-stepper>
|
|
31
|
+
* <div id="wizard-content">Step 1 content...</div>
|
|
32
|
+
*/
|
|
33
|
+
import { Component } from '../core/component.js';
|
|
34
|
+
export declare class NcStep extends Component {
|
|
35
|
+
static useShadowDOM: boolean;
|
|
36
|
+
static get observedAttributes(): string[];
|
|
37
|
+
template(): string;
|
|
38
|
+
}
|
|
39
|
+
export declare class NcStepper extends Component {
|
|
40
|
+
static useShadowDOM: boolean;
|
|
41
|
+
static get observedAttributes(): string[];
|
|
42
|
+
private _getSteps;
|
|
43
|
+
template(): string;
|
|
44
|
+
onMount(): void;
|
|
45
|
+
private _bindEvents;
|
|
46
|
+
next(): void;
|
|
47
|
+
prev(): void;
|
|
48
|
+
goTo(index: number): void;
|
|
49
|
+
attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
|
|
50
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NcStepper + NcStep Components
|
|
3
|
+
*
|
|
4
|
+
* Multi-step wizard with numbered step indicators.
|
|
5
|
+
*
|
|
6
|
+
* nc-stepper:
|
|
7
|
+
* - step: number - current step index (0-based, default: 0)
|
|
8
|
+
* - orientation: 'horizontal'|'vertical' (default: 'horizontal')
|
|
9
|
+
* - variant: 'default'|'simple' (default: 'default')
|
|
10
|
+
* - linear: boolean - prevent skipping ahead (default: false)
|
|
11
|
+
*
|
|
12
|
+
* nc-step:
|
|
13
|
+
* - label: string - step label
|
|
14
|
+
* - description: string - optional sub-label
|
|
15
|
+
* - status: 'complete'|'error'|'' - force a status icon (auto-set by stepper normally)
|
|
16
|
+
*
|
|
17
|
+
* Events (on nc-stepper):
|
|
18
|
+
* - change: CustomEvent<{ step: number; prev: number }>
|
|
19
|
+
*
|
|
20
|
+
* Methods (call on the element):
|
|
21
|
+
* stepper.next()
|
|
22
|
+
* stepper.prev()
|
|
23
|
+
* stepper.goTo(index)
|
|
24
|
+
*
|
|
25
|
+
* Usage:
|
|
26
|
+
* <nc-stepper id="wizard" step="0">
|
|
27
|
+
* <nc-step label="Account" description="Basic info"></nc-step>
|
|
28
|
+
* <nc-step label="Profile"></nc-step>
|
|
29
|
+
* <nc-step label="Review"></nc-step>
|
|
30
|
+
* </nc-stepper>
|
|
31
|
+
* <div id="wizard-content">Step 1 content...</div>
|
|
32
|
+
*/
|
|
33
|
+
import { Component, defineComponent } from '../core/component.js';
|
|
34
|
+
const CHECK_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" width="14" height="14"><path d="M3 8l4 4 6-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
35
|
+
const ERROR_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" width="14" height="14"><path d="M8 5v4M8 11v1" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>`;
|
|
36
|
+
// ── NcStep ───────────────────────────────────────────────────────────────────
|
|
37
|
+
export class NcStep extends Component {
|
|
38
|
+
static useShadowDOM = false; // light DOM - stepper queries children directly
|
|
39
|
+
static get observedAttributes() { return ['label', 'description', 'status']; }
|
|
40
|
+
template() { return ''; } // rendered by NcStepper
|
|
41
|
+
}
|
|
42
|
+
defineComponent('nc-step', NcStep);
|
|
43
|
+
// ── NcStepper ────────────────────────────────────────────────────────────────
|
|
44
|
+
export class NcStepper extends Component {
|
|
45
|
+
static useShadowDOM = true;
|
|
46
|
+
static get observedAttributes() {
|
|
47
|
+
return ['step', 'orientation', 'variant', 'linear'];
|
|
48
|
+
}
|
|
49
|
+
_getSteps() {
|
|
50
|
+
return Array.from(this.querySelectorAll('nc-step'));
|
|
51
|
+
}
|
|
52
|
+
template() {
|
|
53
|
+
const current = Number(this.getAttribute('step') || 0);
|
|
54
|
+
const orientation = this.getAttribute('orientation') || 'horizontal';
|
|
55
|
+
const isHorizontal = orientation === 'horizontal';
|
|
56
|
+
const steps = this._getSteps();
|
|
57
|
+
const total = steps.length;
|
|
58
|
+
const stepItems = steps.map((step, i) => {
|
|
59
|
+
const label = step.getAttribute('label') || `Step ${i + 1}`;
|
|
60
|
+
const desc = step.getAttribute('description') || '';
|
|
61
|
+
const forcedStatus = step.getAttribute('status') || '';
|
|
62
|
+
const isDone = forcedStatus === 'complete' || (!forcedStatus && i < current);
|
|
63
|
+
const isError = forcedStatus === 'error';
|
|
64
|
+
const isActive = i === current;
|
|
65
|
+
let stateClass = 'step--pending';
|
|
66
|
+
if (isActive)
|
|
67
|
+
stateClass = 'step--active';
|
|
68
|
+
else if (isDone)
|
|
69
|
+
stateClass = 'step--done';
|
|
70
|
+
else if (isError)
|
|
71
|
+
stateClass = 'step--error';
|
|
72
|
+
const iconContent = isDone
|
|
73
|
+
? CHECK_ICON
|
|
74
|
+
: isError
|
|
75
|
+
? ERROR_ICON
|
|
76
|
+
: String(i + 1);
|
|
77
|
+
return `
|
|
78
|
+
<div
|
|
79
|
+
class="step ${stateClass}"
|
|
80
|
+
data-index="${i}"
|
|
81
|
+
role="tab"
|
|
82
|
+
aria-selected="${isActive}"
|
|
83
|
+
aria-label="Step ${i + 1}: ${label}"
|
|
84
|
+
tabindex="${isActive ? '0' : '-1'}"
|
|
85
|
+
>
|
|
86
|
+
<div class="step__indicator">${iconContent}</div>
|
|
87
|
+
<div class="step__text">
|
|
88
|
+
<span class="step__label">${label}</span>
|
|
89
|
+
${desc ? `<span class="step__desc">${desc}</span>` : ''}
|
|
90
|
+
</div>
|
|
91
|
+
${i < total - 1 ? `<div class="step__connector"></div>` : ''}
|
|
92
|
+
</div>`;
|
|
93
|
+
}).join('');
|
|
94
|
+
return `
|
|
95
|
+
<style>
|
|
96
|
+
:host { display: block; font-family: var(--nc-font-family); }
|
|
97
|
+
|
|
98
|
+
.stepper {
|
|
99
|
+
display: flex;
|
|
100
|
+
flex-direction: ${isHorizontal ? 'row' : 'column'};
|
|
101
|
+
gap: 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.step {
|
|
105
|
+
display: flex;
|
|
106
|
+
flex-direction: ${isHorizontal ? 'column' : 'row'};
|
|
107
|
+
align-items: ${isHorizontal ? 'center' : 'flex-start'};
|
|
108
|
+
flex: ${isHorizontal ? '1' : 'none'};
|
|
109
|
+
position: relative;
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
gap: ${isHorizontal ? '6px' : 'var(--nc-spacing-sm)'};
|
|
112
|
+
padding: ${isHorizontal ? '0 8px' : 'var(--nc-spacing-sm) 0'};
|
|
113
|
+
outline: none;
|
|
114
|
+
}
|
|
115
|
+
.step:first-child { padding-left: 0; }
|
|
116
|
+
.step:last-child { padding-right: 0; }
|
|
117
|
+
|
|
118
|
+
.step__indicator {
|
|
119
|
+
width: 32px;
|
|
120
|
+
height: 32px;
|
|
121
|
+
border-radius: 50%;
|
|
122
|
+
display: flex;
|
|
123
|
+
align-items: center;
|
|
124
|
+
justify-content: center;
|
|
125
|
+
font-size: var(--nc-font-size-sm);
|
|
126
|
+
font-weight: var(--nc-font-weight-semibold);
|
|
127
|
+
flex-shrink: 0;
|
|
128
|
+
border: 2px solid var(--nc-border);
|
|
129
|
+
background: var(--nc-bg);
|
|
130
|
+
color: var(--nc-text-muted);
|
|
131
|
+
transition: background var(--nc-transition-fast), border-color var(--nc-transition-fast), color var(--nc-transition-fast);
|
|
132
|
+
z-index: 1;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.step--active .step__indicator { border-color: var(--nc-primary); background: var(--nc-primary); color: #fff; }
|
|
136
|
+
.step--done .step__indicator { border-color: var(--nc-primary); background: var(--nc-primary); color: #fff; }
|
|
137
|
+
.step--error .step__indicator { border-color: var(--nc-danger, #ef4444); background: var(--nc-danger, #ef4444); color: #fff; }
|
|
138
|
+
|
|
139
|
+
.step__text {
|
|
140
|
+
display: flex;
|
|
141
|
+
flex-direction: column;
|
|
142
|
+
align-items: ${isHorizontal ? 'center' : 'flex-start'};
|
|
143
|
+
text-align: ${isHorizontal ? 'center' : 'left'};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.step__label {
|
|
147
|
+
font-size: var(--nc-font-size-sm);
|
|
148
|
+
font-weight: var(--nc-font-weight-medium);
|
|
149
|
+
color: var(--nc-text-muted);
|
|
150
|
+
white-space: nowrap;
|
|
151
|
+
}
|
|
152
|
+
.step--active .step__label { color: var(--nc-text); font-weight: var(--nc-font-weight-semibold); }
|
|
153
|
+
.step--done .step__label { color: var(--nc-text); }
|
|
154
|
+
.step--error .step__label { color: var(--nc-danger, #ef4444); }
|
|
155
|
+
|
|
156
|
+
.step__desc {
|
|
157
|
+
font-size: var(--nc-font-size-xs);
|
|
158
|
+
color: var(--nc-text-muted);
|
|
159
|
+
white-space: nowrap;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* Connector line */
|
|
163
|
+
.step__connector {
|
|
164
|
+
position: absolute;
|
|
165
|
+
background: var(--nc-border);
|
|
166
|
+
transition: background var(--nc-transition-fast);
|
|
167
|
+
${isHorizontal
|
|
168
|
+
? `top: 16px; left: calc(50% + 20px); right: calc(-50% + 20px); height: 2px;`
|
|
169
|
+
: `top: 36px; left: 15px; width: 2px; height: calc(100% - 4px);`}
|
|
170
|
+
}
|
|
171
|
+
.step--done .step__connector,
|
|
172
|
+
.step--active .step__connector { background: var(--nc-primary); }
|
|
173
|
+
</style>
|
|
174
|
+
<div class="stepper" role="tablist" aria-orientation="${orientation}">
|
|
175
|
+
${stepItems}
|
|
176
|
+
</div>
|
|
177
|
+
`;
|
|
178
|
+
}
|
|
179
|
+
onMount() {
|
|
180
|
+
this._bindEvents();
|
|
181
|
+
}
|
|
182
|
+
_bindEvents() {
|
|
183
|
+
this.$('.stepper').addEventListener('click', (e) => {
|
|
184
|
+
const stepEl = e.target.closest('[data-index]');
|
|
185
|
+
if (!stepEl)
|
|
186
|
+
return;
|
|
187
|
+
const index = Number(stepEl.dataset.index);
|
|
188
|
+
const current = Number(this.getAttribute('step') || 0);
|
|
189
|
+
if (this.hasAttribute('linear') && index > current)
|
|
190
|
+
return;
|
|
191
|
+
this.goTo(index);
|
|
192
|
+
});
|
|
193
|
+
this.$('.stepper').addEventListener('keydown', (e) => {
|
|
194
|
+
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
|
|
195
|
+
e.preventDefault();
|
|
196
|
+
this.next();
|
|
197
|
+
}
|
|
198
|
+
if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
|
|
199
|
+
e.preventDefault();
|
|
200
|
+
this.prev();
|
|
201
|
+
}
|
|
202
|
+
if (e.key === 'Home')
|
|
203
|
+
this.goTo(0);
|
|
204
|
+
if (e.key === 'End')
|
|
205
|
+
this.goTo(this._getSteps().length - 1);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
next() {
|
|
209
|
+
const current = Number(this.getAttribute('step') || 0);
|
|
210
|
+
this.goTo(Math.min(current + 1, this._getSteps().length - 1));
|
|
211
|
+
}
|
|
212
|
+
prev() {
|
|
213
|
+
const current = Number(this.getAttribute('step') || 0);
|
|
214
|
+
this.goTo(Math.max(current - 1, 0));
|
|
215
|
+
}
|
|
216
|
+
goTo(index) {
|
|
217
|
+
const steps = this._getSteps();
|
|
218
|
+
if (index < 0 || index >= steps.length)
|
|
219
|
+
return;
|
|
220
|
+
const prev = Number(this.getAttribute('step') || 0);
|
|
221
|
+
if (index === prev)
|
|
222
|
+
return;
|
|
223
|
+
this.setAttribute('step', String(index));
|
|
224
|
+
this.dispatchEvent(new CustomEvent('change', {
|
|
225
|
+
bubbles: true, composed: true,
|
|
226
|
+
detail: { step: index, prev }
|
|
227
|
+
}));
|
|
228
|
+
}
|
|
229
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
230
|
+
if (oldValue !== newValue && this._mounted) {
|
|
231
|
+
this.render();
|
|
232
|
+
this._bindEvents();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
defineComponent('nc-stepper', NcStepper);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Component } from '../core/component.js';
|
|
2
|
+
export declare class NcSwitch extends Component {
|
|
3
|
+
static useShadowDOM: boolean;
|
|
4
|
+
static attributeOptions: {
|
|
5
|
+
variant: string[];
|
|
6
|
+
size: string[];
|
|
7
|
+
'label-position': string[];
|
|
8
|
+
};
|
|
9
|
+
static get observedAttributes(): string[];
|
|
10
|
+
template(): string;
|
|
11
|
+
onMount(): void;
|
|
12
|
+
private toggle;
|
|
13
|
+
attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
|
|
14
|
+
}
|