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,301 @@
1
+ /**
2
+ * NcPopover Component - floating panel anchored to a trigger element
3
+ *
4
+ * More flexible than a dropdown: supports arbitrary slot content,
5
+ * arrow pointer, multiple placement options, and click/hover triggers.
6
+ *
7
+ * Attributes:
8
+ * placement - 'top'|'bottom'(default)|'left'|'right'
9
+ * + '-start' or '-end' suffix: 'bottom-start'|'top-end' etc.
10
+ * trigger - 'click'(default)|'hover'|'focus'|'manual'
11
+ * open - boolean - controlled open state
12
+ * offset - gap between anchor and popover in px (default: 8)
13
+ * arrow - boolean - show arrow pointer (default: true)
14
+ * width - popover width CSS value (default: 'auto')
15
+ * max-width - CSS value (default: '320px')
16
+ * close-on-outside - boolean(default true) - close on outside click
17
+ * disabled - boolean
18
+ * hover-delay - ms before hover-trigger opens (default: 200)
19
+ *
20
+ * Slots:
21
+ * trigger - the anchor element
22
+ * (default) - popover content
23
+ *
24
+ * Events:
25
+ * open - popover opened
26
+ * close - popover closed
27
+ *
28
+ * Methods:
29
+ * el.show() / el.hide() / el.toggle()
30
+ *
31
+ * Usage:
32
+ * <nc-popover placement="bottom-start">
33
+ * <nc-button slot="trigger">Info</nc-button>
34
+ * <div style="padding:12px">
35
+ * <p>Popover content here.</p>
36
+ * </div>
37
+ * </nc-popover>
38
+ */
39
+ import { Component, defineComponent } from '../core/component.js';
40
+ export class NcPopover extends Component {
41
+ static useShadowDOM = true;
42
+ _open = false;
43
+ _hoverTimer = null;
44
+ _outside = null;
45
+ static get observedAttributes() { return ['open', 'placement', 'disabled']; }
46
+ template() {
47
+ const open = this._open;
48
+ const arrow = this.getAttribute('arrow') !== 'false';
49
+ const width = this.getAttribute('width') ?? 'auto';
50
+ const maxWidth = this.getAttribute('max-width') ?? '320px';
51
+ return `
52
+ <style>
53
+ :host { display: inline-block; position: relative; }
54
+ .trigger-wrap { display: contents; }
55
+ .popover {
56
+ position: absolute;
57
+ z-index: 1000;
58
+ background: var(--nc-bg-elevated, var(--nc-bg));
59
+ border: 1px solid var(--nc-border);
60
+ border-radius: var(--nc-radius-lg);
61
+ box-shadow: var(--nc-shadow-lg);
62
+ width: ${width};
63
+ max-width: ${maxWidth};
64
+ font-family: var(--nc-font-family);
65
+ font-size: var(--nc-font-size-sm);
66
+ color: var(--nc-text);
67
+ opacity: ${open ? 1 : 0};
68
+ pointer-events: ${open ? 'auto' : 'none'};
69
+ transform: ${open ? 'scale(1) translateY(0)' : 'scale(0.97) translateY(-4px)'};
70
+ transform-origin: top center;
71
+ transition:
72
+ opacity var(--nc-transition-fast),
73
+ transform var(--nc-transition-fast);
74
+ white-space: normal;
75
+ }
76
+ /* Placement styles applied via JS in _position() */
77
+ .arrow {
78
+ display: ${arrow ? 'block' : 'none'};
79
+ position: absolute;
80
+ width: 8px;
81
+ height: 8px;
82
+ background: var(--nc-bg-elevated, var(--nc-bg));
83
+ border: 1px solid var(--nc-border);
84
+ transform: rotate(45deg);
85
+ pointer-events: none;
86
+ }
87
+ </style>
88
+ <div class="trigger-wrap">
89
+ <slot name="trigger"></slot>
90
+ </div>
91
+ <div class="popover" id="popover" role="dialog" aria-modal="false">
92
+ ${arrow ? '<div class="arrow" id="arrow"></div>' : ''}
93
+ <slot></slot>
94
+ </div>
95
+ `;
96
+ }
97
+ onMount() {
98
+ this._bindTrigger();
99
+ if (this.hasAttribute('open')) {
100
+ this._open = true;
101
+ this.render();
102
+ }
103
+ this._position();
104
+ }
105
+ onUnmount() {
106
+ this._cleanup();
107
+ }
108
+ // ── Public API ──────────────────────────────────────────────────────────
109
+ show() { if (!this._open) {
110
+ this._open = true;
111
+ this.render();
112
+ this._position();
113
+ this._setupOutside();
114
+ this.dispatchEvent(new CustomEvent('open', { bubbles: true, composed: true }));
115
+ } }
116
+ hide() { if (this._open) {
117
+ this._open = false;
118
+ this.render();
119
+ this._cleanupOutside();
120
+ this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true }));
121
+ } }
122
+ toggle() {
123
+ if (this._open)
124
+ this.hide();
125
+ else
126
+ this.show();
127
+ }
128
+ // ── Trigger binding ─────────────────────────────────────────────────────
129
+ _bindTrigger() {
130
+ const triggerSlot = this.shadowRoot.querySelector('slot[name="trigger"]');
131
+ if (!triggerSlot)
132
+ return;
133
+ const getTrigger = () => {
134
+ const els = triggerSlot.assignedElements({ flatten: true });
135
+ return els[0] ?? null;
136
+ };
137
+ const mode = this.getAttribute('trigger') ?? 'click';
138
+ const hoverDelay = parseInt(this.getAttribute('hover-delay') ?? '200', 10);
139
+ if (mode === 'click') {
140
+ triggerSlot.addEventListener('slotchange', () => {
141
+ const el = getTrigger();
142
+ if (el)
143
+ el.addEventListener('click', () => {
144
+ if (this.hasAttribute('disabled'))
145
+ return;
146
+ this.toggle();
147
+ });
148
+ });
149
+ // Also handle if already slotted
150
+ requestAnimationFrame(() => {
151
+ const el = getTrigger();
152
+ if (el)
153
+ el.addEventListener('click', () => {
154
+ if (this.hasAttribute('disabled'))
155
+ return;
156
+ this.toggle();
157
+ });
158
+ });
159
+ }
160
+ if (mode === 'hover') {
161
+ triggerSlot.addEventListener('slotchange', () => {
162
+ const el = getTrigger();
163
+ if (!el)
164
+ return;
165
+ el.addEventListener('mouseenter', () => {
166
+ if (this.hasAttribute('disabled'))
167
+ return;
168
+ this._hoverTimer = setTimeout(() => this.show(), hoverDelay);
169
+ });
170
+ el.addEventListener('mouseleave', () => {
171
+ if (this._hoverTimer) {
172
+ clearTimeout(this._hoverTimer);
173
+ this._hoverTimer = null;
174
+ }
175
+ this.hide();
176
+ });
177
+ });
178
+ }
179
+ if (mode === 'focus') {
180
+ triggerSlot.addEventListener('slotchange', () => {
181
+ const el = getTrigger();
182
+ if (!el)
183
+ return;
184
+ el.addEventListener('focusin', () => { if (!this.hasAttribute('disabled'))
185
+ this.show(); });
186
+ el.addEventListener('focusout', () => this.hide());
187
+ });
188
+ }
189
+ }
190
+ // ── Position calculation ────────────────────────────────────────────────
191
+ _position() {
192
+ const popover = this.shadowRoot.querySelector('#popover');
193
+ const arrow = this.shadowRoot.querySelector('#arrow');
194
+ if (!popover)
195
+ return;
196
+ const placement = (this.getAttribute('placement') ?? 'bottom');
197
+ const offset = parseInt(this.getAttribute('offset') ?? '8', 10);
198
+ const [side, align = 'center'] = placement.split('-');
199
+ // Reset
200
+ ['top', 'bottom', 'left', 'right'].forEach(s => { popover.style[s] = ''; });
201
+ const arrowSz = 8;
202
+ const fullOff = offset + (this.getAttribute('arrow') !== 'false' ? arrowSz / 2 : 0);
203
+ if (side === 'bottom') {
204
+ popover.style.top = `calc(100% + ${fullOff}px)`;
205
+ popover.style.transformOrigin = 'top center';
206
+ if (align === 'start')
207
+ popover.style.left = '0';
208
+ else if (align === 'end')
209
+ popover.style.right = '0';
210
+ else {
211
+ popover.style.left = '50%';
212
+ popover.style.transform = this._open ? 'translateX(-50%)' : 'translateX(-50%) scale(0.97) translateY(-4px)';
213
+ }
214
+ if (arrow) {
215
+ arrow.style.top = `-${arrowSz / 2}px`;
216
+ arrow.style.left = '16px';
217
+ arrow.style.borderRight = 'none';
218
+ arrow.style.borderBottom = 'none';
219
+ }
220
+ }
221
+ else if (side === 'top') {
222
+ popover.style.bottom = `calc(100% + ${fullOff}px)`;
223
+ popover.style.transformOrigin = 'bottom center';
224
+ if (align === 'start')
225
+ popover.style.left = '0';
226
+ else if (align === 'end')
227
+ popover.style.right = '0';
228
+ else {
229
+ popover.style.left = '50%';
230
+ popover.style.transform = this._open ? 'translateX(-50%)' : 'translateX(-50%) scale(0.97) translateY(4px)';
231
+ }
232
+ if (arrow) {
233
+ arrow.style.bottom = `-${arrowSz / 2}px`;
234
+ arrow.style.left = '16px';
235
+ arrow.style.borderLeft = 'none';
236
+ arrow.style.borderTop = 'none';
237
+ }
238
+ }
239
+ else if (side === 'right') {
240
+ popover.style.left = `calc(100% + ${fullOff}px)`;
241
+ popover.style.top = '0';
242
+ popover.style.transformOrigin = 'left center';
243
+ if (arrow) {
244
+ arrow.style.left = `-${arrowSz / 2}px`;
245
+ arrow.style.top = '12px';
246
+ arrow.style.borderRight = 'none';
247
+ arrow.style.borderTop = 'none';
248
+ }
249
+ }
250
+ else { // left
251
+ popover.style.right = `calc(100% + ${fullOff}px)`;
252
+ popover.style.top = '0';
253
+ popover.style.transformOrigin = 'right center';
254
+ if (arrow) {
255
+ arrow.style.right = `-${arrowSz / 2}px`;
256
+ arrow.style.top = '12px';
257
+ arrow.style.borderLeft = 'none';
258
+ arrow.style.borderBottom = 'none';
259
+ }
260
+ }
261
+ }
262
+ _setupOutside() {
263
+ if (this.getAttribute('close-on-outside') === 'false')
264
+ return;
265
+ const handler = (e) => {
266
+ if (!this.contains(e.target))
267
+ this.hide();
268
+ };
269
+ document.addEventListener('mousedown', handler);
270
+ this._outside = handler;
271
+ }
272
+ _cleanupOutside() {
273
+ if (this._outside) {
274
+ document.removeEventListener('mousedown', this._outside);
275
+ this._outside = null;
276
+ }
277
+ }
278
+ _cleanup() {
279
+ this._cleanupOutside();
280
+ if (this._hoverTimer)
281
+ clearTimeout(this._hoverTimer);
282
+ }
283
+ attributeChangedCallback(n, o, v) {
284
+ if (o === v || !this._mounted)
285
+ return;
286
+ if (n === 'open') {
287
+ this._open = this.hasAttribute('open');
288
+ this.render();
289
+ if (this._open) {
290
+ this._position();
291
+ this._setupOutside();
292
+ }
293
+ else
294
+ this._cleanupOutside();
295
+ return;
296
+ }
297
+ this.render();
298
+ this._position();
299
+ }
300
+ }
301
+ defineComponent('nc-popover', NcPopover);
@@ -0,0 +1,7 @@
1
+ import { Component } from '../core/component.js';
2
+ export declare class NcProgressCircular extends Component {
3
+ static useShadowDOM: boolean;
4
+ static get observedAttributes(): string[];
5
+ template(): string;
6
+ attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
7
+ }
@@ -0,0 +1,67 @@
1
+ import { Component, defineComponent } from '../core/component.js';
2
+ const VARIANT_COLORS = {
3
+ primary: 'var(--nc-primary, #10b981)',
4
+ success: 'var(--nc-success, #10b981)',
5
+ warning: 'var(--nc-warning, #f59e0b)',
6
+ danger: 'var(--nc-danger, #ef4444)',
7
+ neutral: 'var(--nc-text-muted, #6b7280)'
8
+ };
9
+ export class NcProgressCircular extends Component {
10
+ static useShadowDOM = true;
11
+ static get observedAttributes() {
12
+ return ['value', 'max', 'size', 'thickness', 'variant', 'show-value', 'indeterminate', 'label'];
13
+ }
14
+ template() {
15
+ const max = Number(this.getAttribute('max') || 100);
16
+ const value = Math.min(max, Math.max(0, Number(this.getAttribute('value') || 0)));
17
+ const pct = max > 0 ? Math.round((value / max) * 100) : 0;
18
+ const sizeAttr = this.getAttribute('size') || '48';
19
+ const size = Number(sizeAttr) || 48;
20
+ const thickness = Number(this.getAttribute('thickness') || 4);
21
+ const variant = this.getAttribute('variant') || 'primary';
22
+ const showValue = this.hasAttribute('show-value');
23
+ const indeterminate = this.hasAttribute('indeterminate');
24
+ const label = this.getAttribute('label') || (indeterminate ? 'Loading' : `${pct}%`);
25
+ const color = VARIANT_COLORS[variant] ?? VARIANT_COLORS.primary;
26
+ const radius = (size - thickness) / 2;
27
+ const circumference = 2 * Math.PI * radius;
28
+ const dashOffset = indeterminate ? circumference * 0.25 : circumference * (1 - pct / 100);
29
+ const centerX = size / 2;
30
+ const centerY = size / 2;
31
+ const fontSize = Math.max(10, Math.round(size * 0.22));
32
+ return `
33
+ <style>
34
+ :host { display: inline-flex; align-items: center; justify-content: center; }
35
+ svg { display: block; }
36
+ .track { fill: none; stroke: var(--nc-bg-tertiary, #e5e7eb); }
37
+ .fill { fill: none; stroke: ${color}; stroke-linecap: round; transition: stroke-dashoffset 0.4s ease; }
38
+ ${indeterminate ? `
39
+ @keyframes nc-spin {
40
+ from { transform: rotate(0deg); }
41
+ to { transform: rotate(360deg); }
42
+ }
43
+ svg { animation: nc-spin 1s linear infinite; transform-origin: center; }
44
+ .fill { stroke-dashoffset: ${dashOffset}; }
45
+ ` : ''}
46
+ .label {
47
+ font-family: var(--nc-font-family);
48
+ font-size: ${fontSize}px;
49
+ font-weight: var(--nc-font-weight-semibold, 600);
50
+ fill: var(--nc-text, #111827);
51
+ dominant-baseline: central;
52
+ text-anchor: middle;
53
+ }
54
+ </style>
55
+ <svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" role="progressbar" aria-label="${label}" aria-valuenow="${indeterminate ? '' : value}" aria-valuemin="0" aria-valuemax="${max}">
56
+ <circle class="track" cx="${centerX}" cy="${centerY}" r="${radius}" stroke-width="${thickness}" />
57
+ <circle class="fill" cx="${centerX}" cy="${centerY}" r="${radius}" stroke-width="${thickness}" stroke-dasharray="${circumference}" stroke-dashoffset="${dashOffset}" transform="rotate(-90 ${centerX} ${centerY})" />
58
+ ${showValue && !indeterminate ? `<text class="label" x="${centerX}" y="${centerY}">${pct}%</text>` : ''}
59
+ </svg>
60
+ `;
61
+ }
62
+ attributeChangedCallback(name, oldValue, newValue) {
63
+ if (oldValue !== newValue && this._mounted)
64
+ this.render();
65
+ }
66
+ }
67
+ defineComponent('nc-progress-circular', NcProgressCircular);
@@ -0,0 +1,7 @@
1
+ import { Component } from '../core/component.js';
2
+ export declare class NcProgress extends Component {
3
+ static useShadowDOM: boolean;
4
+ static get observedAttributes(): string[];
5
+ template(): string;
6
+ attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
7
+ }
@@ -0,0 +1,109 @@
1
+ import { Component, defineComponent } from '../core/component.js';
2
+ export class NcProgress extends Component {
3
+ static useShadowDOM = true;
4
+ static get observedAttributes() {
5
+ return ['value', 'max', 'variant', 'size', 'label', 'show-value', 'indeterminate', 'striped', 'animated'];
6
+ }
7
+ template() {
8
+ const max = Number(this.getAttribute('max') || 100);
9
+ const value = Math.min(max, Math.max(0, Number(this.getAttribute('value') || 0)));
10
+ const percentage = max > 0 ? Math.round((value / max) * 100) : 0;
11
+ const variant = this.getAttribute('variant') || 'primary';
12
+ const showValue = this.hasAttribute('show-value');
13
+ const indeterminate = this.hasAttribute('indeterminate');
14
+ const striped = this.hasAttribute('striped');
15
+ const animated = this.hasAttribute('animated');
16
+ const label = this.getAttribute('label') || `${percentage}%`;
17
+ return `
18
+ <style>
19
+ :host { display: block; width: 100%; font-family: var(--nc-font-family); }
20
+
21
+ .wrap { display: flex; align-items: center; gap: var(--nc-spacing-sm, 0.5rem); }
22
+
23
+ .track {
24
+ flex: 1;
25
+ background: var(--nc-bg-tertiary, #e5e7eb);
26
+ border-radius: 999px;
27
+ overflow: hidden;
28
+ position: relative;
29
+ }
30
+
31
+ :host([size="xs"]) .track { height: 4px; }
32
+ :host([size="sm"]) .track,
33
+ :host .track { height: 6px; }
34
+ :host([size="md"]) .track { height: 10px; }
35
+ :host([size="lg"]) .track { height: 16px; }
36
+
37
+ .bar {
38
+ height: 100%;
39
+ border-radius: 999px;
40
+ transition: width 0.4s ease;
41
+ width: ${indeterminate ? '40%' : `${percentage}%`};
42
+ }
43
+
44
+ .bar--primary { background: var(--nc-primary, #10b981); }
45
+ .bar--success { background: var(--nc-success, #10b981); }
46
+ .bar--warning { background: var(--nc-warning, #f59e0b); }
47
+ .bar--danger { background: var(--nc-danger, #ef4444); }
48
+ .bar--neutral { background: var(--nc-text-muted, #6b7280); }
49
+
50
+ ${striped ? `
51
+ .bar {
52
+ background-image: linear-gradient(
53
+ 45deg,
54
+ rgba(255,255,255,.15) 25%,
55
+ transparent 25%,
56
+ transparent 50%,
57
+ rgba(255,255,255,.15) 50%,
58
+ rgba(255,255,255,.15) 75%,
59
+ transparent 75%,
60
+ transparent
61
+ );
62
+ background-size: 1rem 1rem;
63
+ }` : ''}
64
+
65
+ ${animated ? `
66
+ @keyframes nc-progress-stripes {
67
+ from { background-position: 1rem 0; }
68
+ to { background-position: 0 0; }
69
+ }
70
+ .bar { animation: nc-progress-stripes 1s linear infinite; }` : ''}
71
+
72
+ ${indeterminate ? `
73
+ @keyframes nc-indeterminate {
74
+ 0% { left: -40%; }
75
+ 100% { left: 100%; }
76
+ }
77
+ .bar {
78
+ position: absolute;
79
+ animation: nc-indeterminate 1.4s ease infinite;
80
+ }` : ''}
81
+
82
+ .value-label {
83
+ font-size: var(--nc-font-size-xs, 0.75rem);
84
+ color: var(--nc-text-muted, #6b7280);
85
+ min-width: 2.8ch;
86
+ text-align: right;
87
+ }
88
+ </style>
89
+ <div class="wrap">
90
+ <div
91
+ class="track"
92
+ role="progressbar"
93
+ aria-label="${label}"
94
+ aria-valuenow="${indeterminate ? '' : value}"
95
+ aria-valuemin="0"
96
+ aria-valuemax="${max}"
97
+ >
98
+ <div class="bar bar--${variant}"></div>
99
+ </div>
100
+ ${showValue && !indeterminate ? `<span class="value-label">${percentage}%</span>` : ''}
101
+ </div>
102
+ `;
103
+ }
104
+ attributeChangedCallback(name, oldValue, newValue) {
105
+ if (oldValue !== newValue && this._mounted)
106
+ this.render();
107
+ }
108
+ }
109
+ defineComponent('nc-progress', NcProgress);
@@ -0,0 +1,13 @@
1
+ import { Component } from '../core/component.js';
2
+ export declare class NcRadio extends Component {
3
+ static useShadowDOM: boolean;
4
+ static attributeOptions: {
5
+ variant: string[];
6
+ size: string[];
7
+ };
8
+ static get observedAttributes(): string[];
9
+ template(): string;
10
+ onMount(): void;
11
+ private select;
12
+ attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
13
+ }
@@ -0,0 +1,169 @@
1
+ import { Component, defineComponent } from '../core/component.js';
2
+ export class NcRadio extends Component {
3
+ static useShadowDOM = true;
4
+ static attributeOptions = {
5
+ variant: ['primary', 'success', 'danger'],
6
+ size: ['sm', 'md', 'lg']
7
+ };
8
+ static get observedAttributes() {
9
+ return ['label', 'name', 'value', 'checked', 'disabled', 'size', 'variant'];
10
+ }
11
+ template() {
12
+ const label = this.getAttribute('label') || '';
13
+ const size = this.getAttribute('size') || 'md';
14
+ const checked = this.hasAttribute('checked');
15
+ const disabled = this.hasAttribute('disabled');
16
+ const dotSize = size === 'sm' ? '6px' : size === 'lg' ? '10px' : '8px';
17
+ const boxSize = size === 'sm' ? '16px' : size === 'lg' ? '24px' : '20px';
18
+ return `
19
+ <style>
20
+ :host {
21
+ display: inline-flex;
22
+ align-items: center;
23
+ gap: var(--nc-spacing-sm, 0.5rem);
24
+ cursor: ${disabled ? 'not-allowed' : 'pointer'};
25
+ user-select: none;
26
+ font-family: var(--nc-font-family);
27
+ opacity: ${disabled ? '0.5' : '1'};
28
+ }
29
+ .radio-wrapper {
30
+ display: inline-flex;
31
+ align-items: center;
32
+ gap: var(--nc-spacing-sm, 0.5rem);
33
+ }
34
+ input[type="radio"] {
35
+ position: absolute;
36
+ opacity: 0;
37
+ width: 0;
38
+ height: 0;
39
+ pointer-events: none;
40
+ }
41
+ .ring {
42
+ display: inline-flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ flex-shrink: 0;
46
+ width: ${boxSize};
47
+ height: ${boxSize};
48
+ border-radius: var(--nc-radius-full, 9999px);
49
+ border: 2px solid var(--nc-border-dark, #9ca3af);
50
+ background: var(--nc-bg, #ffffff);
51
+ transition: all var(--nc-transition-fast, 160ms ease);
52
+ box-sizing: border-box;
53
+ position: relative;
54
+ }
55
+ .dot {
56
+ width: ${dotSize};
57
+ height: ${dotSize};
58
+ border-radius: var(--nc-radius-full, 9999px);
59
+ background: var(--nc-white, #ffffff);
60
+ opacity: 0;
61
+ transform: scale(0);
62
+ transition: all var(--nc-transition-fast, 160ms ease);
63
+ }
64
+ :host([checked]) .ring {
65
+ border-color: var(--nc-primary, #10b981);
66
+ background: var(--nc-primary, #10b981);
67
+ }
68
+ :host([checked]) .dot {
69
+ opacity: 1;
70
+ transform: scale(1);
71
+ }
72
+ :host([variant="success"][checked]) .ring {
73
+ border-color: var(--nc-success, #10b981);
74
+ background: var(--nc-success, #10b981);
75
+ }
76
+ :host([variant="danger"][checked]) .ring {
77
+ border-color: var(--nc-danger, #ef4444);
78
+ background: var(--nc-danger, #ef4444);
79
+ }
80
+ :host(:not([disabled])) .ring:hover {
81
+ border-color: var(--nc-primary, #10b981);
82
+ box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.15);
83
+ }
84
+ :host([variant="success"]:not([disabled])) .ring:hover {
85
+ border-color: var(--nc-success, #10b981);
86
+ box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.15);
87
+ }
88
+ :host([variant="danger"]:not([disabled])) .ring:hover {
89
+ border-color: var(--nc-danger, #ef4444);
90
+ box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.15);
91
+ }
92
+ :host(:focus-visible) .ring {
93
+ outline: 2px solid var(--nc-primary, #10b981);
94
+ outline-offset: 2px;
95
+ }
96
+ .label {
97
+ font-size: var(--nc-font-size-base, 1rem);
98
+ color: var(--nc-text, #111827);
99
+ line-height: var(--nc-line-height-normal, 1.5);
100
+ }
101
+ :host([size="sm"]) .label {
102
+ font-size: var(--nc-font-size-sm, 0.875rem);
103
+ }
104
+ :host([size="lg"]) .label {
105
+ font-size: var(--nc-font-size-lg, 1.125rem);
106
+ }
107
+ </style>
108
+
109
+ <label class="radio-wrapper">
110
+ <input type="radio" ${checked ? 'checked' : ''} ${disabled ? 'disabled' : ''} name="${this.getAttribute('name') || ''}" value="${this.getAttribute('value') || ''}" />
111
+ <span class="ring"><span class="dot"></span></span>
112
+ ${label ? `<span class="label">${label}</span>` : '<slot></slot>'}
113
+ </label>
114
+ `;
115
+ }
116
+ onMount() {
117
+ if (!this.hasAttribute('tabindex')) {
118
+ this.setAttribute('tabindex', '0');
119
+ }
120
+ this.setAttribute('role', 'radio');
121
+ this.setAttribute('aria-checked', String(this.hasAttribute('checked')));
122
+ this.shadowRoot?.addEventListener('click', () => {
123
+ if (this.hasAttribute('disabled'))
124
+ return;
125
+ this.select();
126
+ });
127
+ this.addEventListener('keydown', (event) => {
128
+ if (event.key === ' ' || event.key === 'Enter') {
129
+ event.preventDefault();
130
+ if (!this.hasAttribute('disabled'))
131
+ this.select();
132
+ }
133
+ });
134
+ }
135
+ select() {
136
+ if (this.hasAttribute('checked'))
137
+ return;
138
+ const name = this.getAttribute('name');
139
+ if (name) {
140
+ const root = this.getRootNode();
141
+ const siblings = Array.from(root.querySelectorAll(`nc-radio[name="${name}"]`));
142
+ siblings.forEach(sibling => {
143
+ if (sibling !== this) {
144
+ sibling.removeAttribute('checked');
145
+ sibling.setAttribute('aria-checked', 'false');
146
+ }
147
+ });
148
+ }
149
+ this.setAttribute('checked', '');
150
+ this.setAttribute('aria-checked', 'true');
151
+ this.dispatchEvent(new CustomEvent('change', {
152
+ bubbles: true,
153
+ composed: true,
154
+ detail: {
155
+ value: this.getAttribute('value') || '',
156
+ name: this.getAttribute('name') || ''
157
+ }
158
+ }));
159
+ }
160
+ attributeChangedCallback(name, oldValue, newValue) {
161
+ if (oldValue !== newValue) {
162
+ this.render();
163
+ if (name === 'checked') {
164
+ this.setAttribute('aria-checked', String(this.hasAttribute('checked')));
165
+ }
166
+ }
167
+ }
168
+ }
169
+ defineComponent('nc-radio', NcRadio);