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.
Files changed (145) hide show
  1. package/README.md +22 -0
  2. package/dist/components/builtinRegistry.d.ts +2 -0
  3. package/dist/components/builtinRegistry.js +72 -0
  4. package/dist/components/index.d.ts +59 -0
  5. package/dist/components/index.js +59 -0
  6. package/dist/components/loading-spinner.d.ts +5 -0
  7. package/dist/components/loading-spinner.js +48 -0
  8. package/dist/components/nc-a.d.ts +45 -0
  9. package/dist/components/nc-a.js +290 -0
  10. package/dist/components/nc-accordion.d.ts +36 -0
  11. package/dist/components/nc-accordion.js +186 -0
  12. package/dist/components/nc-alert.d.ts +11 -0
  13. package/dist/components/nc-alert.js +127 -0
  14. package/dist/components/nc-animation.d.ts +117 -0
  15. package/dist/components/nc-animation.js +1053 -0
  16. package/dist/components/nc-autocomplete.d.ts +41 -0
  17. package/dist/components/nc-autocomplete.js +275 -0
  18. package/dist/components/nc-avatar-group.d.ts +7 -0
  19. package/dist/components/nc-avatar-group.js +85 -0
  20. package/dist/components/nc-avatar.d.ts +9 -0
  21. package/dist/components/nc-avatar.js +127 -0
  22. package/dist/components/nc-badge.d.ts +7 -0
  23. package/dist/components/nc-badge.js +63 -0
  24. package/dist/components/nc-bottom-nav.d.ts +53 -0
  25. package/dist/components/nc-bottom-nav.js +198 -0
  26. package/dist/components/nc-breadcrumb.d.ts +10 -0
  27. package/dist/components/nc-breadcrumb.js +71 -0
  28. package/dist/components/nc-button.d.ts +38 -0
  29. package/dist/components/nc-button.js +293 -0
  30. package/dist/components/nc-card.d.ts +11 -0
  31. package/dist/components/nc-card.js +74 -0
  32. package/dist/components/nc-checkbox.d.ts +16 -0
  33. package/dist/components/nc-checkbox.js +194 -0
  34. package/dist/components/nc-chip.d.ts +8 -0
  35. package/dist/components/nc-chip.js +89 -0
  36. package/dist/components/nc-code.d.ts +37 -0
  37. package/dist/components/nc-code.js +315 -0
  38. package/dist/components/nc-collapsible.d.ts +33 -0
  39. package/dist/components/nc-collapsible.js +148 -0
  40. package/dist/components/nc-color-picker.d.ts +33 -0
  41. package/dist/components/nc-color-picker.js +265 -0
  42. package/dist/components/nc-copy-button.d.ts +10 -0
  43. package/dist/components/nc-copy-button.js +94 -0
  44. package/dist/components/nc-date-picker.d.ts +41 -0
  45. package/dist/components/nc-date-picker.js +443 -0
  46. package/dist/components/nc-div.d.ts +53 -0
  47. package/dist/components/nc-div.js +270 -0
  48. package/dist/components/nc-divider.d.ts +7 -0
  49. package/dist/components/nc-divider.js +57 -0
  50. package/dist/components/nc-drawer.d.ts +40 -0
  51. package/dist/components/nc-drawer.js +217 -0
  52. package/dist/components/nc-dropdown.d.ts +41 -0
  53. package/dist/components/nc-dropdown.js +170 -0
  54. package/dist/components/nc-empty-state.d.ts +5 -0
  55. package/dist/components/nc-empty-state.js +76 -0
  56. package/dist/components/nc-file-upload.d.ts +40 -0
  57. package/dist/components/nc-file-upload.js +336 -0
  58. package/dist/components/nc-form.d.ts +70 -0
  59. package/dist/components/nc-form.js +273 -0
  60. package/dist/components/nc-image.d.ts +10 -0
  61. package/dist/components/nc-image.js +139 -0
  62. package/dist/components/nc-input.d.ts +25 -0
  63. package/dist/components/nc-input.js +302 -0
  64. package/dist/components/nc-kbd.d.ts +5 -0
  65. package/dist/components/nc-kbd.js +34 -0
  66. package/dist/components/nc-menu-item.d.ts +43 -0
  67. package/dist/components/nc-menu-item.js +182 -0
  68. package/dist/components/nc-menu.d.ts +76 -0
  69. package/dist/components/nc-menu.js +360 -0
  70. package/dist/components/nc-modal.d.ts +51 -0
  71. package/dist/components/nc-modal.js +231 -0
  72. package/dist/components/nc-nav-item.d.ts +35 -0
  73. package/dist/components/nc-nav-item.js +142 -0
  74. package/dist/components/nc-number-input.d.ts +22 -0
  75. package/dist/components/nc-number-input.js +270 -0
  76. package/dist/components/nc-otp-input.d.ts +41 -0
  77. package/dist/components/nc-otp-input.js +227 -0
  78. package/dist/components/nc-pagination.d.ts +28 -0
  79. package/dist/components/nc-pagination.js +171 -0
  80. package/dist/components/nc-popover.d.ts +58 -0
  81. package/dist/components/nc-popover.js +301 -0
  82. package/dist/components/nc-progress-circular.d.ts +7 -0
  83. package/dist/components/nc-progress-circular.js +67 -0
  84. package/dist/components/nc-progress.d.ts +7 -0
  85. package/dist/components/nc-progress.js +109 -0
  86. package/dist/components/nc-radio.d.ts +13 -0
  87. package/dist/components/nc-radio.js +169 -0
  88. package/dist/components/nc-rating.d.ts +19 -0
  89. package/dist/components/nc-rating.js +187 -0
  90. package/dist/components/nc-rich-text.d.ts +43 -0
  91. package/dist/components/nc-rich-text.js +310 -0
  92. package/dist/components/nc-scroll-top.d.ts +28 -0
  93. package/dist/components/nc-scroll-top.js +103 -0
  94. package/dist/components/nc-select.d.ts +51 -0
  95. package/dist/components/nc-select.js +425 -0
  96. package/dist/components/nc-skeleton.d.ts +7 -0
  97. package/dist/components/nc-skeleton.js +90 -0
  98. package/dist/components/nc-slider.d.ts +41 -0
  99. package/dist/components/nc-slider.js +268 -0
  100. package/dist/components/nc-snackbar.d.ts +51 -0
  101. package/dist/components/nc-snackbar.js +200 -0
  102. package/dist/components/nc-splash.d.ts +25 -0
  103. package/dist/components/nc-splash.js +296 -0
  104. package/dist/components/nc-stepper.d.ts +50 -0
  105. package/dist/components/nc-stepper.js +236 -0
  106. package/dist/components/nc-switch.d.ts +14 -0
  107. package/dist/components/nc-switch.js +194 -0
  108. package/dist/components/nc-tab-item.d.ts +39 -0
  109. package/dist/components/nc-tab-item.js +127 -0
  110. package/dist/components/nc-table.d.ts +44 -0
  111. package/dist/components/nc-table.js +265 -0
  112. package/dist/components/nc-tabs.d.ts +79 -0
  113. package/dist/components/nc-tabs.js +519 -0
  114. package/dist/components/nc-tag-input.d.ts +49 -0
  115. package/dist/components/nc-tag-input.js +268 -0
  116. package/dist/components/nc-textarea.d.ts +15 -0
  117. package/dist/components/nc-textarea.js +164 -0
  118. package/dist/components/nc-time-picker.d.ts +51 -0
  119. package/dist/components/nc-time-picker.js +452 -0
  120. package/dist/components/nc-timeline.d.ts +53 -0
  121. package/dist/components/nc-timeline.js +171 -0
  122. package/dist/components/nc-tooltip.d.ts +27 -0
  123. package/dist/components/nc-tooltip.js +135 -0
  124. package/dist/core/component.d.ts +33 -0
  125. package/dist/core/component.js +208 -0
  126. package/dist/core/gpu-animation.d.ts +141 -0
  127. package/dist/core/gpu-animation.js +474 -0
  128. package/dist/core/lazyComponents.d.ts +13 -0
  129. package/dist/core/lazyComponents.js +73 -0
  130. package/dist/core/router.d.ts +55 -0
  131. package/dist/core/router.js +424 -0
  132. package/dist/core/state.d.ts +18 -0
  133. package/dist/core/state.js +153 -0
  134. package/dist/index.d.ts +14 -0
  135. package/dist/index.js +11 -0
  136. package/dist/utils/cacheBuster.d.ts +9 -0
  137. package/dist/utils/cacheBuster.js +12 -0
  138. package/dist/utils/dom.d.ts +16 -0
  139. package/dist/utils/dom.js +70 -0
  140. package/dist/utils/events.d.ts +20 -0
  141. package/dist/utils/events.js +80 -0
  142. package/dist/utils/templates.d.ts +2 -0
  143. package/dist/utils/templates.js +2 -0
  144. package/package.json +53 -0
  145. package/src/styles/base.css +40 -0
@@ -0,0 +1,424 @@
1
+ import { bustCache } from '../utils/cacheBuster.js';
2
+ export class Router {
3
+ routes = {};
4
+ currentRoute = null;
5
+ middlewares = [];
6
+ htmlCache = new Map();
7
+ pageScripts = {};
8
+ navigationController = null;
9
+ isNavigating = false;
10
+ renderedLayoutPath = null;
11
+ constructor() {
12
+ if ('scrollRestoration' in window.history) {
13
+ window.history.scrollRestoration = 'manual';
14
+ }
15
+ window.addEventListener('popstate', (e) => {
16
+ this.handleRoute(window.location.pathname, e.state);
17
+ });
18
+ document.addEventListener('click', (e) => {
19
+ const target = e.target;
20
+ const link = target.closest('a');
21
+ if (link && link.tagName === 'A') {
22
+ const href = link.getAttribute('href');
23
+ if (!href)
24
+ return;
25
+ if (href.startsWith('http://') || href.startsWith('https://') ||
26
+ href.startsWith('mailto:') || href.startsWith('tel:') ||
27
+ href.startsWith('#')) {
28
+ return;
29
+ }
30
+ if (link.target === '_blank' || link.hasAttribute('data-external')) {
31
+ return;
32
+ }
33
+ e.preventDefault();
34
+ this.navigate(href);
35
+ }
36
+ });
37
+ }
38
+ register(path, htmlFile, controller = null, options = {}) {
39
+ this.routes[path] = { htmlFile, controller, ...options };
40
+ return this;
41
+ }
42
+ cache(policy) {
43
+ const paths = Object.keys(this.routes);
44
+ const last = paths[paths.length - 1];
45
+ if (last)
46
+ this.routes[last].cachePolicy = policy;
47
+ return this;
48
+ }
49
+ bustCache(path) {
50
+ if (path) {
51
+ const config = this.routes[path];
52
+ if (config) {
53
+ this.htmlCache.delete(config.htmlFile);
54
+ if (config.layout) {
55
+ const layoutConfig = this.routes[config.layout];
56
+ if (layoutConfig) {
57
+ this.htmlCache.delete(layoutConfig.htmlFile);
58
+ }
59
+ }
60
+ }
61
+ }
62
+ else {
63
+ this.htmlCache.clear();
64
+ }
65
+ }
66
+ async prefetch(path) {
67
+ const route = this.matchRoute(path);
68
+ if (!route)
69
+ return;
70
+ const requests = [
71
+ this.fetchHTML(route.config.htmlFile, route.config.cachePolicy, true)
72
+ ];
73
+ const layoutRoute = this.getLayoutRoute(route);
74
+ if (layoutRoute) {
75
+ requests.push(this.fetchHTML(layoutRoute.config.htmlFile, layoutRoute.config.cachePolicy, true));
76
+ }
77
+ await Promise.allSettled(requests);
78
+ }
79
+ use(middleware) {
80
+ this.middlewares.push(middleware);
81
+ return this;
82
+ }
83
+ navigate(path, state = {}) {
84
+ const browserPath = this.normalizeBrowserPath(path);
85
+ if (this.navigationController) {
86
+ this.navigationController.abort();
87
+ }
88
+ this.navigationController = new AbortController();
89
+ window.history.pushState(state, '', browserPath);
90
+ this.handleRoute(browserPath, state);
91
+ }
92
+ replace(path, state = {}) {
93
+ const browserPath = this.normalizeBrowserPath(path);
94
+ window.history.replaceState(state, '', browserPath);
95
+ this.handleRoute(browserPath, state);
96
+ }
97
+ reload() {
98
+ this.isNavigating = false;
99
+ if (this.navigationController) {
100
+ this.navigationController.abort();
101
+ }
102
+ const currentPath = window.location.pathname;
103
+ this.handleRoute(currentPath, {});
104
+ }
105
+ back() {
106
+ window.history.back();
107
+ }
108
+ async handleRoute(path, state = {}) {
109
+ if (this.isNavigating && this.navigationController?.signal.aborted === false) {
110
+ return;
111
+ }
112
+ this.isNavigating = true;
113
+ const route = this.matchRoute(path);
114
+ if (!route) {
115
+ this.handle404(path);
116
+ this.isNavigating = false;
117
+ return;
118
+ }
119
+ if (this.navigationController?.signal.aborted) {
120
+ this.isNavigating = false;
121
+ return;
122
+ }
123
+ for (const middleware of this.middlewares) {
124
+ const result = await middleware(route, state);
125
+ if (result === false) {
126
+ this.isNavigating = false;
127
+ return;
128
+ }
129
+ }
130
+ if (this.navigationController?.signal.aborted) {
131
+ this.isNavigating = false;
132
+ return;
133
+ }
134
+ const previousRoute = this.currentRoute;
135
+ this.currentRoute = route;
136
+ await this.loadPage(route, state, previousRoute);
137
+ this.isNavigating = false;
138
+ }
139
+ async loadPage(route, state = {}, previousRoute = null) {
140
+ const mainContent = document.getElementById('main-content');
141
+ const progressBar = document.getElementById('page-progress');
142
+ if (!mainContent) {
143
+ console.error('main-content element not found');
144
+ return;
145
+ }
146
+ try {
147
+ const isPrerenderedInitialRoute = previousRoute === null &&
148
+ mainContent.getAttribute('data-prerendered-route') === route.path &&
149
+ !route.config.layout;
150
+ if (progressBar) {
151
+ progressBar.classList.add('loading');
152
+ }
153
+ this.resetScrollPosition(mainContent);
154
+ if (previousRoute?.path && this.pageScripts[previousRoute.path]?.cleanup) {
155
+ this.pageScripts[previousRoute.path].cleanup?.();
156
+ }
157
+ if (!isPrerenderedInitialRoute) {
158
+ mainContent.classList.add('page-transition-exit');
159
+ await new Promise(resolve => setTimeout(resolve, 50));
160
+ const contentTarget = await this.resolveContentTarget(mainContent, route);
161
+ const html = await this.fetchHTML(route.config.htmlFile, route.config.cachePolicy);
162
+ contentTarget.innerHTML = html;
163
+ mainContent.classList.remove('page-transition-exit');
164
+ mainContent.classList.add('page-transition-enter');
165
+ }
166
+ if (route.config.controller) {
167
+ const cleanup = await route.config.controller(route.params, state);
168
+ this.pageScripts[route.path] = {
169
+ cleanup: typeof cleanup === 'function' ? cleanup : undefined
170
+ };
171
+ }
172
+ if (isPrerenderedInitialRoute) {
173
+ mainContent.removeAttribute('data-prerendered-route');
174
+ }
175
+ window.dispatchEvent(new CustomEvent('pageloaded', { detail: route }));
176
+ this.resetScrollPosition(mainContent);
177
+ if (progressBar) {
178
+ setTimeout(() => progressBar.classList.remove('loading'), 200);
179
+ }
180
+ if (!isPrerenderedInitialRoute) {
181
+ setTimeout(() => {
182
+ mainContent.classList.remove('page-transition-enter');
183
+ }, 150);
184
+ }
185
+ }
186
+ catch (error) {
187
+ console.error('Error loading page:', error);
188
+ if (progressBar) {
189
+ progressBar.classList.remove('loading');
190
+ }
191
+ this.handle404(route.path);
192
+ }
193
+ }
194
+ async fetchHTML(file, policy, allowPrefetchCache = false) {
195
+ const entry = this.htmlCache.get(file);
196
+ const now = Date.now();
197
+ const ttlMs = this.resolveCacheTtl(policy, entry) * 1000;
198
+ const isFresh = entry && ttlMs > 0 && now - entry.cachedAt < ttlMs;
199
+ const isStale = entry && ttlMs > 0 && !isFresh;
200
+ if (isStale && policy?.revalidate) {
201
+ this.refreshInBackground(file, policy.ttl);
202
+ return entry.html;
203
+ }
204
+ if (isFresh)
205
+ return entry.html;
206
+ const response = await fetch(bustCache(file), { cache: 'no-store' });
207
+ if (!response.ok)
208
+ throw new Error(`Failed to load ${file}`);
209
+ const html = await response.text();
210
+ if (ttlMs > 0 || allowPrefetchCache) {
211
+ this.htmlCache.set(file, {
212
+ html,
213
+ cachedAt: now,
214
+ ttl: policy?.ttl ?? entry?.ttl ?? 30
215
+ });
216
+ }
217
+ return html;
218
+ }
219
+ async refreshInBackground(file, ttl) {
220
+ try {
221
+ const response = await fetch(bustCache(file), { cache: 'no-store' });
222
+ if (!response.ok)
223
+ return;
224
+ const html = await response.text();
225
+ this.htmlCache.set(file, { html, cachedAt: Date.now(), ttl });
226
+ }
227
+ catch {
228
+ }
229
+ }
230
+ matchRoute(path) {
231
+ const normalizedPath = this.normalizeRoutePath(path);
232
+ if (this.routes[normalizedPath]) {
233
+ return { path: normalizedPath, params: {}, config: this.routes[normalizedPath] };
234
+ }
235
+ for (const [routePath, config] of Object.entries(this.routes)) {
236
+ const params = this.extractParams(routePath, normalizedPath);
237
+ if (params) {
238
+ return { path: routePath, params, config };
239
+ }
240
+ }
241
+ return null;
242
+ }
243
+ extractParams(routePath, actualPath) {
244
+ const routeParts = this.splitPath(routePath);
245
+ const actualParts = this.splitPath(actualPath);
246
+ const params = {};
247
+ let routeIndex = 0;
248
+ let actualIndex = 0;
249
+ while (routeIndex < routeParts.length) {
250
+ const routePart = routeParts[routeIndex];
251
+ const actualPart = actualParts[actualIndex];
252
+ if (routePart === '*') {
253
+ params.wildcard = actualParts.slice(actualIndex).join('/');
254
+ actualIndex = actualParts.length;
255
+ routeIndex++;
256
+ continue;
257
+ }
258
+ if (routePart.startsWith(':') && routePart.endsWith('?')) {
259
+ const paramName = routePart.slice(1, -1);
260
+ if (actualPart !== undefined) {
261
+ params[paramName] = actualPart;
262
+ actualIndex++;
263
+ }
264
+ routeIndex++;
265
+ continue;
266
+ }
267
+ if (actualPart === undefined) {
268
+ return null;
269
+ }
270
+ if (routePart.startsWith(':')) {
271
+ params[routePart.slice(1)] = actualPart;
272
+ routeIndex++;
273
+ actualIndex++;
274
+ continue;
275
+ }
276
+ if (routePart !== actualPart) {
277
+ return null;
278
+ }
279
+ routeIndex++;
280
+ actualIndex++;
281
+ }
282
+ return actualIndex === actualParts.length ? params : null;
283
+ }
284
+ handle404(path) {
285
+ const mainContent = document.getElementById('main-content');
286
+ if (mainContent) {
287
+ this.resetScrollPosition(mainContent);
288
+ mainContent.innerHTML = `
289
+ <div style="
290
+ display: flex;
291
+ flex-direction: column;
292
+ align-items: center;
293
+ justify-content: center;
294
+ min-height: 60vh;
295
+ text-align: center;
296
+ padding: var(--spacing-xl, 2rem);
297
+ ">
298
+ <div style="
299
+ font-size: 8rem;
300
+ font-weight: 700;
301
+ color: var(--primary, #0f766e);
302
+ line-height: 1;
303
+ margin-bottom: var(--spacing-md, 1rem);
304
+ ">404</div>
305
+ <h1 style="
306
+ font-size: 2rem;
307
+ font-weight: 600;
308
+ color: var(--text-primary, #111827);
309
+ margin-bottom: var(--spacing-sm, 0.75rem);
310
+ ">Page Not Found</h1>
311
+ <p style="
312
+ font-size: 1.1rem;
313
+ color: var(--text-secondary, #4b5563);
314
+ max-width: 500px;
315
+ margin-bottom: var(--spacing-lg, 1.5rem);
316
+ ">
317
+ The page <code style="
318
+ background: var(--background-secondary, #f3f4f6);
319
+ padding: 0.2rem 0.5rem;
320
+ border-radius: var(--radius-sm, 0.375rem);
321
+ color: var(--primary, #0f766e);
322
+ ">${path}</code> could not be found.
323
+ </p>
324
+ <button onclick="window.history.back()" style="
325
+ display: inline-flex;
326
+ align-items: center;
327
+ gap: 0.5rem;
328
+ padding: var(--spacing-sm, 0.75rem) var(--spacing-lg, 1.5rem);
329
+ background: var(--primary, #0f766e);
330
+ color: white;
331
+ border: none;
332
+ border-radius: var(--radius-md, 0.5rem);
333
+ font-weight: 500;
334
+ font-size: 1rem;
335
+ cursor: pointer;
336
+ transition: all 0.2s ease;
337
+ " onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='var(--shadow-md, 0 10px 20px rgba(0,0,0,0.12))'"
338
+ onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none'">
339
+ <span><</span> Go Back
340
+ </button>
341
+ </div>
342
+ `;
343
+ }
344
+ }
345
+ start() {
346
+ const browserPath = this.normalizeBrowserPath(window.location.pathname + window.location.search + window.location.hash);
347
+ if (browserPath !== window.location.pathname + window.location.search + window.location.hash) {
348
+ window.history.replaceState(window.history.state, '', browserPath);
349
+ }
350
+ this.handleRoute(browserPath);
351
+ }
352
+ getCurrentRoute() {
353
+ return this.currentRoute;
354
+ }
355
+ async resolveContentTarget(mainContent, route) {
356
+ const layoutRoute = this.getLayoutRoute(route);
357
+ if (!layoutRoute) {
358
+ this.renderedLayoutPath = null;
359
+ return mainContent;
360
+ }
361
+ const needsLayoutRender = this.renderedLayoutPath !== layoutRoute.path ||
362
+ !mainContent.querySelector('#route-outlet');
363
+ if (needsLayoutRender) {
364
+ const layoutHtml = await this.fetchHTML(layoutRoute.config.htmlFile, layoutRoute.config.cachePolicy);
365
+ mainContent.innerHTML = layoutHtml;
366
+ this.renderedLayoutPath = layoutRoute.path;
367
+ }
368
+ const outlet = mainContent.querySelector('#route-outlet');
369
+ if (!outlet) {
370
+ throw new Error(`Layout route "${layoutRoute.path}" is missing a #route-outlet element`);
371
+ }
372
+ return outlet;
373
+ }
374
+ getLayoutRoute(route) {
375
+ const layoutPath = route.config.layout;
376
+ if (!layoutPath)
377
+ return null;
378
+ const layoutConfig = this.routes[layoutPath];
379
+ if (!layoutConfig) {
380
+ throw new Error(`Layout route "${layoutPath}" is not registered`);
381
+ }
382
+ return {
383
+ path: layoutPath,
384
+ params: {},
385
+ config: layoutConfig
386
+ };
387
+ }
388
+ splitPath(path) {
389
+ const trimmed = path.replace(/^\/+|\/+$/g, '');
390
+ return trimmed ? trimmed.split('/') : [];
391
+ }
392
+ normalizeRoutePath(path) {
393
+ const cleanedPath = path.replace(/[?#].*$/, '').replace(/\/+$/, '');
394
+ return cleanedPath || '/';
395
+ }
396
+ normalizeBrowserPath(path) {
397
+ const [pathnameWithQuery, hash = ''] = path.split('#');
398
+ const [pathname = '/', query = ''] = pathnameWithQuery.split('?');
399
+ if (!pathname || pathname === '/' || pathname.endsWith('/') || /\.[a-z0-9]+$/i.test(pathname)) {
400
+ return path;
401
+ }
402
+ const normalizedPath = `${pathname}/`;
403
+ const querySuffix = query ? `?${query}` : '';
404
+ const hashSuffix = hash ? `#${hash}` : '';
405
+ return `${normalizedPath}${querySuffix}${hashSuffix}`;
406
+ }
407
+ resolveCacheTtl(policy, entry) {
408
+ return policy?.ttl ?? entry?.ttl ?? 0;
409
+ }
410
+ resetScrollPosition(mainContent) {
411
+ window.scrollTo(0, 0);
412
+ if (mainContent) {
413
+ mainContent.scrollTop = 0;
414
+ mainContent.scrollLeft = 0;
415
+ const scrollContainer = mainContent.closest('.main-content');
416
+ if (scrollContainer) {
417
+ scrollContainer.scrollTop = 0;
418
+ scrollContainer.scrollLeft = 0;
419
+ }
420
+ }
421
+ }
422
+ }
423
+ const router = new Router();
424
+ export default router;
@@ -0,0 +1,18 @@
1
+ export interface State<T> {
2
+ value: T;
3
+ set(value: T | ((prev: T) => T)): void;
4
+ watch(callback: (value: T) => void): () => void;
5
+ }
6
+ export interface ComputedState<T> {
7
+ readonly value: T;
8
+ watch(callback: (value: T) => void): () => void;
9
+ dispose(): void;
10
+ }
11
+ export type EffectCleanup = void | (() => void);
12
+ export type EffectCallback = () => EffectCleanup;
13
+ export declare function useState<T>(initialValue: T): State<T>;
14
+ export declare function createStates<T extends Record<string, any>>(initialStates: T): {
15
+ [K in keyof T]: State<T[K]>;
16
+ };
17
+ export declare function computed<T>(computeFn: () => T): ComputedState<T>;
18
+ export declare function effect(effectFn: EffectCallback): () => void;
@@ -0,0 +1,153 @@
1
+ let currentTracker = null;
2
+ function createState(initialValue) {
3
+ let currentValue = initialValue;
4
+ const subscribers = new Set();
5
+ const state = {
6
+ get value() {
7
+ return currentValue;
8
+ },
9
+ set value(newValue) {
10
+ if (Object.is(currentValue, newValue))
11
+ return;
12
+ currentValue = newValue;
13
+ notify();
14
+ },
15
+ set(valueOrUpdater) {
16
+ const newValue = typeof valueOrUpdater === 'function'
17
+ ? valueOrUpdater(currentValue)
18
+ : valueOrUpdater;
19
+ if (Object.is(currentValue, newValue))
20
+ return;
21
+ currentValue = newValue;
22
+ notify();
23
+ },
24
+ watch(callback) {
25
+ const wrappedCallback = () => callback(currentValue);
26
+ subscribers.add(wrappedCallback);
27
+ return () => subscribers.delete(wrappedCallback);
28
+ }
29
+ };
30
+ function notify() {
31
+ subscribers.forEach(fn => fn());
32
+ }
33
+ return state;
34
+ }
35
+ export function useState(initialValue) {
36
+ const state = createState(initialValue);
37
+ const trackedState = {
38
+ get value() {
39
+ if (currentTracker) {
40
+ currentTracker.accessed.add(state);
41
+ }
42
+ return state.value;
43
+ },
44
+ set value(newValue) {
45
+ state.value = newValue;
46
+ },
47
+ set: state.set.bind(state),
48
+ watch: state.watch.bind(state)
49
+ };
50
+ return trackedState;
51
+ }
52
+ export function createStates(initialStates) {
53
+ const states = {};
54
+ for (const [key, value] of Object.entries(initialStates)) {
55
+ states[key] = useState(value);
56
+ }
57
+ return states;
58
+ }
59
+ export function computed(computeFn) {
60
+ const derivedState = createState(undefined);
61
+ const trackedDeps = new Set();
62
+ const depUnsubscribers = new Map();
63
+ let isComputing = false;
64
+ const tracker = {
65
+ accessed: new Set()
66
+ };
67
+ recompute();
68
+ function recompute() {
69
+ if (isComputing)
70
+ return;
71
+ isComputing = true;
72
+ tracker.accessed.clear();
73
+ currentTracker = tracker;
74
+ try {
75
+ const newValue = computeFn();
76
+ derivedState.value = newValue;
77
+ }
78
+ finally {
79
+ currentTracker = null;
80
+ isComputing = false;
81
+ }
82
+ syncTrackedDependencies(trackedDeps, tracker.accessed, depUnsubscribers, recompute);
83
+ }
84
+ function dispose() {
85
+ depUnsubscribers.forEach(unsub => unsub());
86
+ depUnsubscribers.clear();
87
+ trackedDeps.clear();
88
+ }
89
+ return {
90
+ get value() {
91
+ if (currentTracker) {
92
+ currentTracker.accessed.add(derivedState);
93
+ }
94
+ return derivedState.value;
95
+ },
96
+ watch: derivedState.watch.bind(derivedState),
97
+ dispose,
98
+ set value(_) {
99
+ console.warn('Computed values are read-only. Cannot set value directly.');
100
+ }
101
+ };
102
+ }
103
+ export function effect(effectFn) {
104
+ const trackedDeps = new Set();
105
+ const depUnsubscribers = new Map();
106
+ const tracker = {
107
+ accessed: new Set()
108
+ };
109
+ let cleanup;
110
+ let isRunning = false;
111
+ runEffect();
112
+ return () => {
113
+ if (typeof cleanup === 'function') {
114
+ cleanup();
115
+ }
116
+ depUnsubscribers.forEach(unsub => unsub());
117
+ depUnsubscribers.clear();
118
+ trackedDeps.clear();
119
+ };
120
+ function runEffect() {
121
+ if (isRunning)
122
+ return;
123
+ isRunning = true;
124
+ tracker.accessed.clear();
125
+ currentTracker = tracker;
126
+ try {
127
+ if (typeof cleanup === 'function') {
128
+ cleanup();
129
+ }
130
+ cleanup = effectFn();
131
+ }
132
+ finally {
133
+ currentTracker = null;
134
+ isRunning = false;
135
+ }
136
+ syncTrackedDependencies(trackedDeps, tracker.accessed, depUnsubscribers, runEffect);
137
+ }
138
+ }
139
+ function syncTrackedDependencies(trackedDeps, nextDeps, depUnsubscribers, onDependencyChange) {
140
+ trackedDeps.forEach(dep => {
141
+ if (!nextDeps.has(dep)) {
142
+ depUnsubscribers.get(dep)?.();
143
+ depUnsubscribers.delete(dep);
144
+ trackedDeps.delete(dep);
145
+ }
146
+ });
147
+ nextDeps.forEach(dep => {
148
+ if (!trackedDeps.has(dep)) {
149
+ trackedDeps.add(dep);
150
+ depUnsubscribers.set(dep, dep.watch(() => onDependencyChange()));
151
+ }
152
+ });
153
+ }
@@ -0,0 +1,14 @@
1
+ export { Component, defineComponent } from './core/component.js';
2
+ export { LoadingSpinner, NcA, NcAccordion, NcAccordionItem, NcAlert, NcAnimation, NcAutocomplete, NcAvatar, NcAvatarGroup, NcBadge, NcBottomNav, NcBottomNavItem, NcBreadcrumb, NcButton, NcCard, NcCheckbox, NcChip, NcCode, NcCollapsible, NcColorPicker, NcCopyButton, NcDatePicker, NcDiv, NcDivider, NcDrawer, NcDropdown, NcEmptyState, NcField, NcFileUpload, NcForm, NcImage, NcInput, NcKbd, NcNumberInput, NcMenu, NcMenuItem, NcModal, NcNavItem, NcOtpInput, NcPagination, NcPopover, NcProgress, NcProgressCircular, NcRadio, NcRating, NcRichText, NcScrollTop, NcSelect, NcSkeleton, NcSlider, NcSnackbar, NcSplash, NcStep, NcStepper, NcSwitch, NcTabItem, NcTable, NcTabs, NcTagInput, NcTextarea, NcTimePicker, NcTimeline, NcTimelineItem, NcTooltip } from './components/index.js';
3
+ export { builtinComponentManifest, registerBuiltinComponents } from './components/builtinRegistry.js';
4
+ export { GPUAnimation, addPassiveListener, animate, cleanupAnimation, createAnimationLoop, fadeIn, fadeOut, prepareForAnimation, rafThrottle, scaleIn, setGPUTransform, setTransformVars, slideIn, throttle } from './core/gpu-animation.js';
5
+ export { Router } from './core/router.js';
6
+ export { componentRegistry, initLazyComponents } from './core/lazyComponents.js';
7
+ export { useState, createStates, computed, effect } from './core/state.js';
8
+ export { bustCache, cacheVersion, importWithBust } from './utils/cacheBuster.js';
9
+ export { dom } from './utils/dom.js';
10
+ export { css, html } from './utils/templates.js';
11
+ export { bindEvents, delegate, on, onChange, onClick, onInput, onSubmit, trackEvents, trackSubscriptions } from './utils/events.js';
12
+ export type { ComponentConstructor, ComponentState } from './core/component.js';
13
+ export type { CachePolicy, ControllerFunction, MiddlewareFunction, RouteConfig, RouteMatch } from './core/router.js';
14
+ export type { ComputedState, EffectCallback, EffectCleanup, State } from './core/state.js';
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ export { Component, defineComponent } from './core/component.js';
2
+ export { LoadingSpinner, NcA, NcAccordion, NcAccordionItem, NcAlert, NcAnimation, NcAutocomplete, NcAvatar, NcAvatarGroup, NcBadge, NcBottomNav, NcBottomNavItem, NcBreadcrumb, NcButton, NcCard, NcCheckbox, NcChip, NcCode, NcCollapsible, NcColorPicker, NcCopyButton, NcDatePicker, NcDiv, NcDivider, NcDrawer, NcDropdown, NcEmptyState, NcField, NcFileUpload, NcForm, NcImage, NcInput, NcKbd, NcNumberInput, NcMenu, NcMenuItem, NcModal, NcNavItem, NcOtpInput, NcPagination, NcPopover, NcProgress, NcProgressCircular, NcRadio, NcRating, NcRichText, NcScrollTop, NcSelect, NcSkeleton, NcSlider, NcSnackbar, NcSplash, NcStep, NcStepper, NcSwitch, NcTabItem, NcTable, NcTabs, NcTagInput, NcTextarea, NcTimePicker, NcTimeline, NcTimelineItem, NcTooltip } from './components/index.js';
3
+ export { builtinComponentManifest, registerBuiltinComponents } from './components/builtinRegistry.js';
4
+ export { GPUAnimation, addPassiveListener, animate, cleanupAnimation, createAnimationLoop, fadeIn, fadeOut, prepareForAnimation, rafThrottle, scaleIn, setGPUTransform, setTransformVars, slideIn, throttle } from './core/gpu-animation.js';
5
+ export { Router } from './core/router.js';
6
+ export { componentRegistry, initLazyComponents } from './core/lazyComponents.js';
7
+ export { useState, createStates, computed, effect } from './core/state.js';
8
+ export { bustCache, cacheVersion, importWithBust } from './utils/cacheBuster.js';
9
+ export { dom } from './utils/dom.js';
10
+ export { css, html } from './utils/templates.js';
11
+ export { bindEvents, delegate, on, onChange, onClick, onInput, onSubmit, trackEvents, trackSubscriptions } from './utils/events.js';
@@ -0,0 +1,9 @@
1
+ export declare const cacheVersion: string | number;
2
+ export declare function bustCache(url: string): string;
3
+ export declare function importWithBust(modulePath: string): Promise<any>;
4
+ declare const _default: {
5
+ cacheVersion: string | number;
6
+ bustCache: typeof bustCache;
7
+ importWithBust: typeof importWithBust;
8
+ };
9
+ export default _default;
@@ -0,0 +1,12 @@
1
+ const isDevelopment = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
2
+ export const cacheVersion = isDevelopment
3
+ ? Date.now()
4
+ : '0.1.0';
5
+ export function bustCache(url) {
6
+ const separator = url.includes('?') ? '&' : '?';
7
+ return `${url}${separator}v=${cacheVersion}`;
8
+ }
9
+ export async function importWithBust(modulePath) {
10
+ return import(bustCache(modulePath));
11
+ }
12
+ export default { cacheVersion, bustCache, importWithBust };
@@ -0,0 +1,16 @@
1
+ export declare const dom: {
2
+ query: <T extends Element = Element>(selector: string) => T | null;
3
+ queryAll: <T extends Element = Element>(selector: string) => NodeListOf<T>;
4
+ $: <T extends Element = Element>(selector: string) => T | null;
5
+ $$: <T extends Element = Element>(selector: string) => NodeListOf<T>;
6
+ within: <T extends Element = Element>(parent: Element | ShadowRoot | string, selector: string) => T | null;
7
+ withinAll: <T extends Element = Element>(parent: Element | ShadowRoot | string, selector: string) => NodeListOf<T> | T[];
8
+ create: <K extends keyof HTMLElementTagNameMap>(tag: K, attrs?: Record<string, string> | null, ...children: Array<string | Node>) => HTMLElementTagNameMap[K];
9
+ addClass: (element: Element | string | null, ...classes: string[]) => void;
10
+ removeClass: (element: Element | string | null, ...classes: string[]) => void;
11
+ toggleClass: (element: Element | string | null, className: string, force?: boolean) => void;
12
+ show: (element: Element | string | null) => void;
13
+ hide: (element: Element | string | null) => void;
14
+ listen: (selectorOrElement: string | Element | null, eventName: string, handler: (event: any) => void, options?: boolean | AddEventListenerOptions) => () => void;
15
+ };
16
+ export default dom;