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,227 @@
1
+ /**
2
+ * NcOtpInput Component - One-time password / verification code input
3
+ *
4
+ * Attributes:
5
+ * length - number of boxes (default: 6)
6
+ * type - 'numeric'(default)|'alphanumeric'|'alpha'
7
+ * separator - insert a visual dash/space separator after this position (e.g. "3" for 3+3)
8
+ * disabled - boolean
9
+ * masked - boolean - mask input like a password
10
+ * autofocus - boolean - focus first box on mount
11
+ * label - accessible label
12
+ * error - error message
13
+ * hint - helper text
14
+ *
15
+ * Value (read/write via property):
16
+ * el.value - get/set current OTP string
17
+ *
18
+ * Events:
19
+ * change - CustomEvent<{ value: string; complete: boolean }>
20
+ * complete - CustomEvent<{ value: string }> - fired when all boxes are filled
21
+ *
22
+ * Usage:
23
+ * <nc-otp-input length="6" type="numeric"></nc-otp-input>
24
+ */
25
+ import { Component, defineComponent } from '../core/component.js';
26
+ export class NcOtpInput extends Component {
27
+ static useShadowDOM = true;
28
+ _values = [];
29
+ static get observedAttributes() { return ['length', 'disabled', 'masked', 'error']; }
30
+ get value() { return this._values.join(''); }
31
+ set value(v) {
32
+ const len = this._length();
33
+ this._values = v.slice(0, len).split('');
34
+ while (this._values.length < len)
35
+ this._values.push('');
36
+ this.render();
37
+ this._bindEvents();
38
+ }
39
+ _length() { return parseInt(this.getAttribute('length') ?? '6', 10); }
40
+ template() {
41
+ const len = this._length();
42
+ const masked = this.hasAttribute('masked');
43
+ const disabled = this.hasAttribute('disabled');
44
+ const label = this.getAttribute('label') ?? '';
45
+ const error = this.getAttribute('error') ?? '';
46
+ const hint = this.getAttribute('hint') ?? '';
47
+ const separator = parseInt(this.getAttribute('separator') ?? '0', 10);
48
+ while (this._values.length < len)
49
+ this._values.push('');
50
+ const boxesHtml = Array.from({ length: len }, (_, i) => {
51
+ const val = this._values[i] ?? '';
52
+ const showSep = separator > 0 && i === separator - 1 && i < len - 1;
53
+ return `
54
+ <input
55
+ class="box"
56
+ type="${masked ? 'password' : 'text'}"
57
+ inputmode="${masked ? 'text' : 'numeric'}"
58
+ maxlength="1"
59
+ data-idx="${i}"
60
+ value="${val}"
61
+ ${disabled ? 'disabled' : ''}
62
+ autocomplete="one-time-code"
63
+ aria-label="${label ? label + ' ' : ''}digit ${i + 1}"
64
+ />
65
+ ${showSep ? '<span class="sep">-</span>' : ''}
66
+ `;
67
+ }).join('');
68
+ return `
69
+ <style>
70
+ :host { display: block; font-family: var(--nc-font-family); }
71
+ .wrap { display: flex; align-items: center; gap: var(--nc-spacing-xs); }
72
+ .box {
73
+ width: 44px;
74
+ height: 52px;
75
+ text-align: center;
76
+ font-size: var(--nc-font-size-xl);
77
+ font-weight: var(--nc-font-weight-semibold);
78
+ color: var(--nc-text);
79
+ background: var(--nc-bg);
80
+ border: 2px solid ${error ? 'var(--nc-danger)' : 'var(--nc-border)'};
81
+ border-radius: var(--nc-radius-md);
82
+ outline: none;
83
+ transition: border-color var(--nc-transition-fast), box-shadow var(--nc-transition-fast);
84
+ caret-color: transparent;
85
+ padding: 0;
86
+ }
87
+ .box:focus {
88
+ border-color: var(--nc-primary);
89
+ box-shadow: 0 0 0 3px rgba(var(--nc-primary-rgb, 99,102,241),.2);
90
+ }
91
+ .box:disabled { opacity: 0.5; cursor: not-allowed; }
92
+ .box.filled { border-color: var(--nc-primary); background: var(--nc-bg-secondary); }
93
+ .sep {
94
+ color: var(--nc-text-muted);
95
+ font-size: var(--nc-font-size-lg);
96
+ font-weight: var(--nc-font-weight-medium);
97
+ user-select: none;
98
+ padding: 0 2px;
99
+ }
100
+ .hint { font-size: var(--nc-font-size-xs); color: var(--nc-text-muted); margin-top: 6px; }
101
+ .error { font-size: var(--nc-font-size-xs); color: var(--nc-danger); margin-top: 6px; }
102
+ </style>
103
+ <div class="wrap" role="group" aria-label="${label || 'OTP input'}">
104
+ ${boxesHtml}
105
+ </div>
106
+ ${error ? `<p class="error">${error}</p>` : hint ? `<p class="hint">${hint}</p>` : ''}
107
+ `;
108
+ }
109
+ onMount() {
110
+ if (this.hasAttribute('autofocus')) {
111
+ requestAnimationFrame(() => this._boxAt(0)?.focus());
112
+ }
113
+ this._bindEvents();
114
+ this._applyFilledClass();
115
+ }
116
+ _bindEvents() {
117
+ const boxes = this._boxes();
118
+ boxes.forEach((box, idx) => {
119
+ // Remove stale listeners by replacing (simple approach via re-render)
120
+ box.addEventListener('focus', () => box.select());
121
+ box.addEventListener('input', () => {
122
+ const type = this.getAttribute('type') ?? 'numeric';
123
+ const input = box;
124
+ let val = input.value;
125
+ // Filter by type
126
+ if (type === 'numeric')
127
+ val = val.replace(/\D/g, '');
128
+ if (type === 'alpha')
129
+ val = val.replace(/[^a-zA-Z]/g, '');
130
+ if (type === 'alphanumeric')
131
+ val = val.replace(/[^a-zA-Z0-9]/g, '');
132
+ val = val.slice(-1).toUpperCase();
133
+ input.value = val;
134
+ this._values[idx] = val;
135
+ this._applyFilledClass();
136
+ this._emitChange();
137
+ if (val && idx < boxes.length - 1)
138
+ this._boxAt(idx + 1)?.focus();
139
+ });
140
+ box.addEventListener('keydown', (e) => {
141
+ const input = box;
142
+ if (e.key === 'Backspace') {
143
+ if (input.value) {
144
+ input.value = '';
145
+ this._values[idx] = '';
146
+ this._applyFilledClass();
147
+ this._emitChange();
148
+ }
149
+ else if (idx > 0) {
150
+ this._boxAt(idx - 1)?.focus();
151
+ }
152
+ e.preventDefault();
153
+ }
154
+ else if (e.key === 'ArrowLeft' && idx > 0)
155
+ this._boxAt(idx - 1)?.focus();
156
+ else if (e.key === 'ArrowRight' && idx < boxes.length - 1)
157
+ this._boxAt(idx + 1)?.focus();
158
+ else if (e.key === 'Delete') {
159
+ input.value = '';
160
+ this._values[idx] = '';
161
+ this._applyFilledClass();
162
+ this._emitChange();
163
+ e.preventDefault();
164
+ }
165
+ });
166
+ // Handle multi-character paste without blocking the native paste event.
167
+ box.addEventListener('paste', (e) => {
168
+ const text = e.clipboardData?.getData('text') ?? '';
169
+ if (!text)
170
+ return;
171
+ let filtered = text;
172
+ const type = this.getAttribute('type') ?? 'numeric';
173
+ if (type === 'numeric')
174
+ filtered = text.replace(/\D/g, '');
175
+ if (type === 'alpha')
176
+ filtered = text.replace(/[^a-zA-Z]/g, '');
177
+ if (type === 'alphanumeric')
178
+ filtered = text.replace(/[^a-zA-Z0-9]/g, '');
179
+ requestAnimationFrame(() => {
180
+ const chars = filtered.toUpperCase().slice(0, this._length() - idx).split('');
181
+ chars.forEach((ch, offset) => {
182
+ const targetIndex = idx + offset;
183
+ this._values[targetIndex] = ch;
184
+ const targetBox = this._boxAt(targetIndex);
185
+ if (targetBox)
186
+ targetBox.value = ch;
187
+ });
188
+ this._applyFilledClass();
189
+ this._emitChange();
190
+ const nextFocus = Math.min(idx + chars.length, boxes.length - 1);
191
+ this._boxAt(nextFocus)?.focus();
192
+ });
193
+ });
194
+ });
195
+ }
196
+ _applyFilledClass() {
197
+ this._boxes().forEach((box, i) => {
198
+ box.classList.toggle('filled', !!(this._values[i]));
199
+ });
200
+ }
201
+ _emitChange() {
202
+ const value = this.value;
203
+ const complete = value.length === this._length() && !value.includes('');
204
+ this.dispatchEvent(new CustomEvent('change', {
205
+ detail: { value, complete }, bubbles: true, composed: true,
206
+ }));
207
+ if (complete) {
208
+ this.dispatchEvent(new CustomEvent('complete', {
209
+ detail: { value }, bubbles: true, composed: true,
210
+ }));
211
+ }
212
+ }
213
+ _boxes() {
214
+ return Array.from(this.shadowRoot.querySelectorAll('.box'));
215
+ }
216
+ _boxAt(i) {
217
+ return this.shadowRoot.querySelector(`.box[data-idx="${i}"]`);
218
+ }
219
+ attributeChangedCallback(name, oldVal, newVal) {
220
+ if (oldVal === newVal || !this._mounted)
221
+ return;
222
+ this.render();
223
+ this._bindEvents();
224
+ this._applyFilledClass();
225
+ }
226
+ }
227
+ defineComponent('nc-otp-input', NcOtpInput);
@@ -0,0 +1,28 @@
1
+ /**
2
+ * NcPagination Component
3
+ *
4
+ * Attributes:
5
+ * - page: number - current page (1-based, default: 1)
6
+ * - total: number - total pages (required)
7
+ * - siblings: number - pages shown on each side of current (default: 1)
8
+ * - show-first-last: boolean - show First/Last buttons
9
+ * - disabled: boolean
10
+ * - size: 'sm'|'md'|'lg' (default: 'md')
11
+ * - variant: 'default'|'outline' (default: 'default')
12
+ *
13
+ * Events:
14
+ * - change: CustomEvent<{ page: number }>
15
+ *
16
+ * Usage:
17
+ * <nc-pagination page="3" total="20"></nc-pagination>
18
+ */
19
+ import { Component } from '../core/component.js';
20
+ export declare class NcPagination extends Component {
21
+ static useShadowDOM: boolean;
22
+ static get observedAttributes(): string[];
23
+ private _buildPages;
24
+ template(): string;
25
+ onMount(): void;
26
+ private _bindEvents;
27
+ attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
28
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * NcPagination Component
3
+ *
4
+ * Attributes:
5
+ * - page: number - current page (1-based, default: 1)
6
+ * - total: number - total pages (required)
7
+ * - siblings: number - pages shown on each side of current (default: 1)
8
+ * - show-first-last: boolean - show First/Last buttons
9
+ * - disabled: boolean
10
+ * - size: 'sm'|'md'|'lg' (default: 'md')
11
+ * - variant: 'default'|'outline' (default: 'default')
12
+ *
13
+ * Events:
14
+ * - change: CustomEvent<{ page: number }>
15
+ *
16
+ * Usage:
17
+ * <nc-pagination page="3" total="20"></nc-pagination>
18
+ */
19
+ import { Component, defineComponent } from '../core/component.js';
20
+ export class NcPagination extends Component {
21
+ static useShadowDOM = true;
22
+ static get observedAttributes() {
23
+ return ['page', 'total', 'siblings', 'show-first-last', 'disabled', 'size', 'variant'];
24
+ }
25
+ _buildPages(current, total, siblings) {
26
+ if (total <= 7)
27
+ return Array.from({ length: total }, (_, i) => i + 1);
28
+ const left = Math.max(2, current - siblings);
29
+ const right = Math.min(total - 1, current + siblings);
30
+ const pages = [1];
31
+ if (left > 2)
32
+ pages.push('...');
33
+ for (let i = left; i <= right; i++)
34
+ pages.push(i);
35
+ if (right < total - 1)
36
+ pages.push('...');
37
+ pages.push(total);
38
+ return pages;
39
+ }
40
+ template() {
41
+ const current = Number(this.getAttribute('page') || 1);
42
+ const total = Number(this.getAttribute('total') || 1);
43
+ const siblings = Number(this.getAttribute('siblings') ?? 1);
44
+ const showFirstLast = this.hasAttribute('show-first-last');
45
+ const disabled = this.hasAttribute('disabled');
46
+ const pages = this._buildPages(current, total, siblings);
47
+ const atFirst = current <= 1;
48
+ const atLast = current >= total;
49
+ const navBtn = (dir, label, dis) => `
50
+ <button class="btn btn--nav" data-dir="${dir}" ${dis || disabled ? 'disabled' : ''} aria-label="${label}">
51
+ ${dir === 'prev' || dir === 'first'
52
+ ? `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" width="14" height="14"><path d="M${dir === 'first' ? '12 3L7 8l5 5M7 3L2 8l5 5' : '10 3L5 8l5 5'}" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`
53
+ : `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" width="14" height="14"><path d="M${dir === 'last' ? '4 3l5 5-5 5M9 3l5 5-5 5' : '6 3l5 5-5 5'}" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`}
54
+ </button>`;
55
+ return `
56
+ <style>
57
+ :host { display: block; font-family: var(--nc-font-family); }
58
+
59
+ .pagination {
60
+ display: inline-flex;
61
+ align-items: center;
62
+ gap: 4px;
63
+ flex-wrap: wrap;
64
+ opacity: ${disabled ? '0.5' : '1'};
65
+ pointer-events: ${disabled ? 'none' : 'auto'};
66
+ }
67
+
68
+ .btn {
69
+ display: inline-flex;
70
+ align-items: center;
71
+ justify-content: center;
72
+ border: 1px solid var(--nc-border);
73
+ background: var(--nc-bg);
74
+ color: var(--nc-text);
75
+ cursor: pointer;
76
+ border-radius: var(--nc-radius-sm, 6px);
77
+ font-family: var(--nc-font-family);
78
+ font-size: var(--nc-font-size-sm);
79
+ transition: background var(--nc-transition-fast), color var(--nc-transition-fast), border-color var(--nc-transition-fast);
80
+ min-width: 36px;
81
+ height: 36px;
82
+ padding: 0 6px;
83
+ }
84
+
85
+ :host([size="sm"]) .btn { min-width: 28px; height: 28px; font-size: var(--nc-font-size-xs); }
86
+ :host([size="lg"]) .btn { min-width: 44px; height: 44px; font-size: var(--nc-font-size-base); }
87
+
88
+ .btn:hover:not(:disabled):not(.btn--active) { background: var(--nc-bg-secondary); border-color: var(--nc-border-dark); }
89
+ .btn:disabled { opacity: 0.4; cursor: not-allowed; }
90
+
91
+ .btn--active {
92
+ background: var(--nc-primary);
93
+ color: #fff;
94
+ border-color: var(--nc-primary);
95
+ font-weight: var(--nc-font-weight-semibold);
96
+ pointer-events: none;
97
+ }
98
+
99
+ :host([variant="outline"]) .btn--active {
100
+ background: transparent;
101
+ color: var(--nc-primary);
102
+ }
103
+
104
+ .ellipsis {
105
+ display: inline-flex;
106
+ align-items: center;
107
+ justify-content: center;
108
+ min-width: 36px;
109
+ height: 36px;
110
+ font-size: var(--nc-font-size-sm);
111
+ color: var(--nc-text-muted);
112
+ }
113
+ </style>
114
+ <nav aria-label="Pagination" class="pagination">
115
+ ${showFirstLast ? navBtn('first', 'First page', atFirst) : ''}
116
+ ${navBtn('prev', 'Previous page', atFirst)}
117
+ ${pages.map(p => p === '...'
118
+ ? `<span class="ellipsis" aria-hidden="true">...</span>`
119
+ : `<button class="btn${p === current ? ' btn--active' : ''}" data-page="${p}" aria-label="Page ${p}" aria-current="${p === current ? 'page' : 'false'}">${p}</button>`).join('')}
120
+ ${navBtn('next', 'Next page', atLast)}
121
+ ${showFirstLast ? navBtn('last', 'Last page', atLast) : ''}
122
+ </nav>
123
+ `;
124
+ }
125
+ onMount() {
126
+ this._bindEvents();
127
+ }
128
+ _bindEvents() {
129
+ this.$('.pagination').addEventListener('click', (e) => {
130
+ const btn = e.target.closest('button');
131
+ if (!btn || btn.disabled)
132
+ return;
133
+ const current = Number(this.getAttribute('page') || 1);
134
+ const total = Number(this.getAttribute('total') || 1);
135
+ let next = current;
136
+ if (btn.dataset.page) {
137
+ next = Number(btn.dataset.page);
138
+ }
139
+ else {
140
+ switch (btn.dataset.dir) {
141
+ case 'first':
142
+ next = 1;
143
+ break;
144
+ case 'prev':
145
+ next = Math.max(1, current - 1);
146
+ break;
147
+ case 'next':
148
+ next = Math.min(total, current + 1);
149
+ break;
150
+ case 'last':
151
+ next = total;
152
+ break;
153
+ }
154
+ }
155
+ if (next !== current) {
156
+ this.setAttribute('page', String(next));
157
+ this.dispatchEvent(new CustomEvent('change', {
158
+ bubbles: true, composed: true,
159
+ detail: { page: next }
160
+ }));
161
+ }
162
+ });
163
+ }
164
+ attributeChangedCallback(name, oldValue, newValue) {
165
+ if (oldValue !== newValue && this._mounted) {
166
+ this.render();
167
+ this._bindEvents();
168
+ }
169
+ }
170
+ }
171
+ defineComponent('nc-pagination', NcPagination);
@@ -0,0 +1,58 @@
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 } from '../core/component.js';
40
+ export declare class NcPopover extends Component {
41
+ static useShadowDOM: boolean;
42
+ private _open;
43
+ private _hoverTimer;
44
+ private _outside;
45
+ static get observedAttributes(): string[];
46
+ template(): string;
47
+ onMount(): void;
48
+ onUnmount(): void;
49
+ show(): void;
50
+ hide(): void;
51
+ toggle(): void;
52
+ private _bindTrigger;
53
+ private _position;
54
+ private _setupOutside;
55
+ private _cleanupOutside;
56
+ private _cleanup;
57
+ attributeChangedCallback(n: string, o: string, v: string): void;
58
+ }