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,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
|
+
}
|