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,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NcAutocomplete Component
|
|
3
|
+
*
|
|
4
|
+
* Attributes:
|
|
5
|
+
* - name: string
|
|
6
|
+
* - value: string - current input value
|
|
7
|
+
* - placeholder: string
|
|
8
|
+
* - options: JSON string array OR comma-separated - static suggestions
|
|
9
|
+
* - min-chars: number - chars before showing suggestions (default: 1)
|
|
10
|
+
* - max-results: number - max visible items (default: 8)
|
|
11
|
+
* - disabled: boolean
|
|
12
|
+
* - size: 'sm'|'md'|'lg' (default: 'md')
|
|
13
|
+
* - variant: 'default'|'filled' (default: 'default')
|
|
14
|
+
*
|
|
15
|
+
* Dynamic options - dispatch 'nc-autocomplete-options' on the element:
|
|
16
|
+
* el.dispatchEvent(new CustomEvent('nc-autocomplete-options', { detail: ['a','b'] }))
|
|
17
|
+
*
|
|
18
|
+
* Events:
|
|
19
|
+
* - input: CustomEvent<{ value: string; name: string }>
|
|
20
|
+
* - select: CustomEvent<{ value: string; name: string }>
|
|
21
|
+
* - change: CustomEvent<{ value: string; name: string }>
|
|
22
|
+
*/
|
|
23
|
+
import { Component } from '../core/component.js';
|
|
24
|
+
export declare class NcAutocomplete extends Component {
|
|
25
|
+
static useShadowDOM: boolean;
|
|
26
|
+
static get observedAttributes(): string[];
|
|
27
|
+
private _inputValue;
|
|
28
|
+
private _dynamicOptions;
|
|
29
|
+
private _activeIndex;
|
|
30
|
+
private _open;
|
|
31
|
+
constructor();
|
|
32
|
+
private _getOptions;
|
|
33
|
+
private _filtered;
|
|
34
|
+
template(): string;
|
|
35
|
+
onMount(): void;
|
|
36
|
+
private _bindEvents;
|
|
37
|
+
private _selectOption;
|
|
38
|
+
private _refreshDropdown;
|
|
39
|
+
private _refreshActive;
|
|
40
|
+
attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
|
|
41
|
+
}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NcAutocomplete Component
|
|
3
|
+
*
|
|
4
|
+
* Attributes:
|
|
5
|
+
* - name: string
|
|
6
|
+
* - value: string - current input value
|
|
7
|
+
* - placeholder: string
|
|
8
|
+
* - options: JSON string array OR comma-separated - static suggestions
|
|
9
|
+
* - min-chars: number - chars before showing suggestions (default: 1)
|
|
10
|
+
* - max-results: number - max visible items (default: 8)
|
|
11
|
+
* - disabled: boolean
|
|
12
|
+
* - size: 'sm'|'md'|'lg' (default: 'md')
|
|
13
|
+
* - variant: 'default'|'filled' (default: 'default')
|
|
14
|
+
*
|
|
15
|
+
* Dynamic options - dispatch 'nc-autocomplete-options' on the element:
|
|
16
|
+
* el.dispatchEvent(new CustomEvent('nc-autocomplete-options', { detail: ['a','b'] }))
|
|
17
|
+
*
|
|
18
|
+
* Events:
|
|
19
|
+
* - input: CustomEvent<{ value: string; name: string }>
|
|
20
|
+
* - select: CustomEvent<{ value: string; name: string }>
|
|
21
|
+
* - change: CustomEvent<{ value: string; name: string }>
|
|
22
|
+
*/
|
|
23
|
+
import { Component, defineComponent } from '../core/component.js';
|
|
24
|
+
export class NcAutocomplete extends Component {
|
|
25
|
+
static useShadowDOM = true;
|
|
26
|
+
static get observedAttributes() {
|
|
27
|
+
return ['name', 'value', 'placeholder', 'options', 'min-chars', 'max-results', 'disabled', 'size', 'variant'];
|
|
28
|
+
}
|
|
29
|
+
_inputValue = '';
|
|
30
|
+
_dynamicOptions = [];
|
|
31
|
+
_activeIndex = -1;
|
|
32
|
+
_open = false;
|
|
33
|
+
constructor() { super(); }
|
|
34
|
+
_getOptions() {
|
|
35
|
+
if (this._dynamicOptions.length)
|
|
36
|
+
return this._dynamicOptions;
|
|
37
|
+
const raw = this.getAttribute('options') || '';
|
|
38
|
+
if (!raw)
|
|
39
|
+
return [];
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(raw);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return raw.split(',').map(s => s.trim()).filter(Boolean);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
_filtered() {
|
|
48
|
+
const query = this._inputValue.trim().toLowerCase();
|
|
49
|
+
const minChars = Number(this.getAttribute('min-chars') ?? 1);
|
|
50
|
+
const max = Number(this.getAttribute('max-results') ?? 8);
|
|
51
|
+
if (query.length < minChars)
|
|
52
|
+
return [];
|
|
53
|
+
return this._getOptions()
|
|
54
|
+
.filter(o => o.toLowerCase().includes(query))
|
|
55
|
+
.slice(0, max);
|
|
56
|
+
}
|
|
57
|
+
template() {
|
|
58
|
+
if (!this._mounted) {
|
|
59
|
+
this._inputValue = this.getAttribute('value') || '';
|
|
60
|
+
}
|
|
61
|
+
const disabled = this.hasAttribute('disabled');
|
|
62
|
+
const placeholder = this.getAttribute('placeholder') || '';
|
|
63
|
+
const name = this.getAttribute('name') || '';
|
|
64
|
+
const results = this._open ? this._filtered() : [];
|
|
65
|
+
return `
|
|
66
|
+
<style>
|
|
67
|
+
:host { display: block; position: relative; width: 100%; font-family: var(--nc-font-family); }
|
|
68
|
+
|
|
69
|
+
.input-wrap {
|
|
70
|
+
position: relative;
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
input {
|
|
76
|
+
width: 100%;
|
|
77
|
+
box-sizing: border-box;
|
|
78
|
+
padding: var(--nc-spacing-sm) var(--nc-spacing-md);
|
|
79
|
+
background: var(--nc-bg);
|
|
80
|
+
border: var(--nc-input-border);
|
|
81
|
+
border-radius: var(--nc-input-radius);
|
|
82
|
+
color: var(--nc-text);
|
|
83
|
+
font-size: var(--nc-font-size-base);
|
|
84
|
+
font-family: var(--nc-font-family);
|
|
85
|
+
outline: none;
|
|
86
|
+
transition: border-color var(--nc-transition-fast), box-shadow var(--nc-transition-fast);
|
|
87
|
+
opacity: ${disabled ? '0.5' : '1'};
|
|
88
|
+
cursor: ${disabled ? 'not-allowed' : 'auto'};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
:host([size="sm"]) input { font-size: var(--nc-font-size-sm); padding: var(--nc-spacing-xs) var(--nc-spacing-sm); }
|
|
92
|
+
:host([size="lg"]) input { font-size: var(--nc-font-size-lg); padding: var(--nc-spacing-md) var(--nc-spacing-lg); }
|
|
93
|
+
:host([variant="filled"]) input { background: var(--nc-bg-tertiary); border-color: transparent; }
|
|
94
|
+
|
|
95
|
+
input:focus { border-color: var(--nc-input-focus-border); box-shadow: 0 0 0 3px rgba(16,185,129,.15); }
|
|
96
|
+
input::placeholder { color: var(--nc-text-muted); }
|
|
97
|
+
|
|
98
|
+
.dropdown {
|
|
99
|
+
position: absolute;
|
|
100
|
+
top: calc(100% + 4px);
|
|
101
|
+
left: 0; right: 0;
|
|
102
|
+
background: var(--nc-bg);
|
|
103
|
+
border: 1px solid var(--nc-border);
|
|
104
|
+
border-radius: var(--nc-radius-md, 8px);
|
|
105
|
+
box-shadow: var(--nc-shadow-lg);
|
|
106
|
+
overflow: hidden;
|
|
107
|
+
z-index: 500;
|
|
108
|
+
display: ${results.length ? 'block' : 'none'};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.option {
|
|
112
|
+
padding: var(--nc-spacing-sm) var(--nc-spacing-md);
|
|
113
|
+
cursor: pointer;
|
|
114
|
+
font-size: var(--nc-font-size-sm);
|
|
115
|
+
color: var(--nc-text);
|
|
116
|
+
transition: background var(--nc-transition-fast);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.option:hover,
|
|
120
|
+
.option.active { background: var(--nc-bg-secondary); }
|
|
121
|
+
|
|
122
|
+
.option mark {
|
|
123
|
+
background: none;
|
|
124
|
+
color: var(--nc-primary);
|
|
125
|
+
font-weight: var(--nc-font-weight-semibold);
|
|
126
|
+
}
|
|
127
|
+
</style>
|
|
128
|
+
<div class="input-wrap">
|
|
129
|
+
<input
|
|
130
|
+
type="text"
|
|
131
|
+
name="${name}"
|
|
132
|
+
value="${this._inputValue}"
|
|
133
|
+
placeholder="${placeholder}"
|
|
134
|
+
${disabled ? 'disabled' : ''}
|
|
135
|
+
autocomplete="off"
|
|
136
|
+
role="combobox"
|
|
137
|
+
aria-expanded="${results.length > 0}"
|
|
138
|
+
aria-autocomplete="list"
|
|
139
|
+
aria-haspopup="listbox"
|
|
140
|
+
/>
|
|
141
|
+
</div>
|
|
142
|
+
<div class="dropdown" role="listbox">
|
|
143
|
+
${results.map((opt, i) => {
|
|
144
|
+
const hl = opt.replace(new RegExp(`(${this._inputValue.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi'), '<mark>$1</mark>');
|
|
145
|
+
return `<div class="option${i === this._activeIndex ? ' active' : ''}" role="option" data-value="${opt}" aria-selected="${i === this._activeIndex}">${hl}</div>`;
|
|
146
|
+
}).join('')}
|
|
147
|
+
</div>
|
|
148
|
+
`;
|
|
149
|
+
}
|
|
150
|
+
onMount() {
|
|
151
|
+
this._bindEvents();
|
|
152
|
+
// Dynamic options API
|
|
153
|
+
this.addEventListener('nc-autocomplete-options', (e) => {
|
|
154
|
+
this._dynamicOptions = e.detail || [];
|
|
155
|
+
if (this._open) {
|
|
156
|
+
this.render();
|
|
157
|
+
this._bindEvents();
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
_bindEvents() {
|
|
162
|
+
const input = this.$('input');
|
|
163
|
+
const dropdown = this.$('.dropdown');
|
|
164
|
+
input.addEventListener('input', () => {
|
|
165
|
+
this._inputValue = input.value;
|
|
166
|
+
this._activeIndex = -1;
|
|
167
|
+
this._open = true;
|
|
168
|
+
this._refreshDropdown();
|
|
169
|
+
this.dispatchEvent(new CustomEvent('input', {
|
|
170
|
+
bubbles: true, composed: true,
|
|
171
|
+
detail: { value: input.value, name: this.getAttribute('name') || '' }
|
|
172
|
+
}));
|
|
173
|
+
});
|
|
174
|
+
input.addEventListener('focus', () => {
|
|
175
|
+
this._open = true;
|
|
176
|
+
this._refreshDropdown();
|
|
177
|
+
});
|
|
178
|
+
input.addEventListener('blur', () => {
|
|
179
|
+
// Delay so click on option fires first
|
|
180
|
+
setTimeout(() => {
|
|
181
|
+
this._open = false;
|
|
182
|
+
this._refreshDropdown();
|
|
183
|
+
}, 150);
|
|
184
|
+
});
|
|
185
|
+
input.addEventListener('keydown', (e) => {
|
|
186
|
+
const results = this._filtered();
|
|
187
|
+
if (!results.length)
|
|
188
|
+
return;
|
|
189
|
+
if (e.key === 'ArrowDown') {
|
|
190
|
+
e.preventDefault();
|
|
191
|
+
this._activeIndex = Math.min(this._activeIndex + 1, results.length - 1);
|
|
192
|
+
this._refreshActive();
|
|
193
|
+
}
|
|
194
|
+
else if (e.key === 'ArrowUp') {
|
|
195
|
+
e.preventDefault();
|
|
196
|
+
this._activeIndex = Math.max(this._activeIndex - 1, -1);
|
|
197
|
+
this._refreshActive();
|
|
198
|
+
}
|
|
199
|
+
else if (e.key === 'Enter' && this._activeIndex >= 0) {
|
|
200
|
+
e.preventDefault();
|
|
201
|
+
this._selectOption(results[this._activeIndex]);
|
|
202
|
+
}
|
|
203
|
+
else if (e.key === 'Escape') {
|
|
204
|
+
this._open = false;
|
|
205
|
+
this._refreshDropdown();
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
dropdown.addEventListener('mousedown', (e) => {
|
|
209
|
+
const opt = e.target.closest('[data-value]');
|
|
210
|
+
if (opt) {
|
|
211
|
+
e.preventDefault();
|
|
212
|
+
this._selectOption(opt.dataset.value);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
_selectOption(value) {
|
|
217
|
+
this._inputValue = value;
|
|
218
|
+
this._open = false;
|
|
219
|
+
this._activeIndex = -1;
|
|
220
|
+
this.setAttribute('value', value);
|
|
221
|
+
const input = this.$('input');
|
|
222
|
+
if (input)
|
|
223
|
+
input.value = value;
|
|
224
|
+
this._refreshDropdown();
|
|
225
|
+
this.dispatchEvent(new CustomEvent('select', {
|
|
226
|
+
bubbles: true, composed: true,
|
|
227
|
+
detail: { value, name: this.getAttribute('name') || '' }
|
|
228
|
+
}));
|
|
229
|
+
this.dispatchEvent(new CustomEvent('change', {
|
|
230
|
+
bubbles: true, composed: true,
|
|
231
|
+
detail: { value, name: this.getAttribute('name') || '' }
|
|
232
|
+
}));
|
|
233
|
+
}
|
|
234
|
+
_refreshDropdown() {
|
|
235
|
+
const dropdown = this.$('.dropdown');
|
|
236
|
+
if (!dropdown)
|
|
237
|
+
return;
|
|
238
|
+
const results = this._open ? this._filtered() : [];
|
|
239
|
+
if (!results.length) {
|
|
240
|
+
dropdown.style.display = 'none';
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
dropdown.style.display = 'block';
|
|
244
|
+
dropdown.innerHTML = results.map((opt, i) => {
|
|
245
|
+
const escaped = this._inputValue.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
246
|
+
const hl = escaped ? opt.replace(new RegExp(`(${escaped})`, 'gi'), '<mark>$1</mark>') : opt;
|
|
247
|
+
return `<div class="option${i === this._activeIndex ? ' active' : ''}" role="option" data-value="${opt}" aria-selected="${i === this._activeIndex}">${hl}</div>`;
|
|
248
|
+
}).join('');
|
|
249
|
+
const input = this.$('input');
|
|
250
|
+
if (input)
|
|
251
|
+
input.setAttribute('aria-expanded', String(results.length > 0));
|
|
252
|
+
}
|
|
253
|
+
_refreshActive() {
|
|
254
|
+
this.$$('.option').forEach((opt, i) => {
|
|
255
|
+
opt.classList.toggle('active', i === this._activeIndex);
|
|
256
|
+
opt.setAttribute('aria-selected', String(i === this._activeIndex));
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
260
|
+
if (oldValue === newValue)
|
|
261
|
+
return;
|
|
262
|
+
if (name === 'value' && this._mounted) {
|
|
263
|
+
this._inputValue = newValue || '';
|
|
264
|
+
const input = this.$('input');
|
|
265
|
+
if (input)
|
|
266
|
+
input.value = this._inputValue;
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (this._mounted) {
|
|
270
|
+
this.render();
|
|
271
|
+
this._bindEvents();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
defineComponent('nc-autocomplete', NcAutocomplete);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Component, defineComponent } from '../core/component.js';
|
|
2
|
+
export class NcAvatarGroup extends Component {
|
|
3
|
+
static useShadowDOM = true;
|
|
4
|
+
template() {
|
|
5
|
+
const overlap = parseInt(this.getAttribute('overlap') ?? '10', 10);
|
|
6
|
+
const size = this.getAttribute('size') ?? 'md';
|
|
7
|
+
const sizePixels = {
|
|
8
|
+
xs: 24,
|
|
9
|
+
sm: 32,
|
|
10
|
+
md: 40,
|
|
11
|
+
lg: 48,
|
|
12
|
+
xl: 56
|
|
13
|
+
};
|
|
14
|
+
const resolvedSize = sizePixels[size] ?? 40;
|
|
15
|
+
const fontSize = Math.round(resolvedSize * 0.32);
|
|
16
|
+
const borderWidth = Math.max(2, Math.round(resolvedSize * 0.06));
|
|
17
|
+
return `
|
|
18
|
+
<style>
|
|
19
|
+
:host { display: inline-block; }
|
|
20
|
+
.group {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: row;
|
|
23
|
+
align-items: center;
|
|
24
|
+
}
|
|
25
|
+
::slotted(*) {
|
|
26
|
+
margin-left: -${overlap}px;
|
|
27
|
+
box-shadow: 0 0 0 ${borderWidth}px var(--nc-bg, #fff);
|
|
28
|
+
border-radius: 50%;
|
|
29
|
+
flex-shrink: 0;
|
|
30
|
+
position: relative;
|
|
31
|
+
transition: transform var(--nc-transition-fast, 160ms ease), z-index 0s;
|
|
32
|
+
z-index: 0;
|
|
33
|
+
}
|
|
34
|
+
::slotted(*:first-child) { margin-left: 0; }
|
|
35
|
+
::slotted(*:hover) { transform: translateY(-3px); z-index: 10; }
|
|
36
|
+
.overflow {
|
|
37
|
+
margin-left: -${overlap}px;
|
|
38
|
+
width: ${resolvedSize}px;
|
|
39
|
+
height: ${resolvedSize}px;
|
|
40
|
+
border-radius: 50%;
|
|
41
|
+
background: var(--nc-bg-tertiary, #e5e7eb);
|
|
42
|
+
color: var(--nc-text-secondary, #4b5563);
|
|
43
|
+
font-family: var(--nc-font-family);
|
|
44
|
+
font-size: ${fontSize}px;
|
|
45
|
+
font-weight: var(--nc-font-weight-semibold, 600);
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
justify-content: center;
|
|
49
|
+
box-shadow: 0 0 0 ${borderWidth}px var(--nc-bg, #fff);
|
|
50
|
+
flex-shrink: 0;
|
|
51
|
+
user-select: none;
|
|
52
|
+
}
|
|
53
|
+
</style>
|
|
54
|
+
<div class="group" role="group" aria-label="Avatar group">
|
|
55
|
+
<slot></slot>
|
|
56
|
+
<div class="overflow" id="overflow" style="display:none" aria-hidden="true"></div>
|
|
57
|
+
</div>
|
|
58
|
+
`;
|
|
59
|
+
}
|
|
60
|
+
onMount() {
|
|
61
|
+
this.updateGroup();
|
|
62
|
+
}
|
|
63
|
+
updateGroup() {
|
|
64
|
+
const slot = this.shadowRoot?.querySelector('slot');
|
|
65
|
+
const overflow = this.shadowRoot?.querySelector('#overflow');
|
|
66
|
+
if (!slot || !overflow)
|
|
67
|
+
return;
|
|
68
|
+
const max = parseInt(this.getAttribute('max') ?? '4', 10);
|
|
69
|
+
const total = parseInt(this.getAttribute('total') ?? '0', 10);
|
|
70
|
+
const items = slot.assignedElements({ flatten: true });
|
|
71
|
+
const count = total > 0 ? total : items.length;
|
|
72
|
+
items.forEach((element, index) => {
|
|
73
|
+
element.style.display = index < max ? '' : 'none';
|
|
74
|
+
});
|
|
75
|
+
const extra = count - Math.min(max, items.length);
|
|
76
|
+
if (extra > 0) {
|
|
77
|
+
overflow.textContent = `+${extra}`;
|
|
78
|
+
overflow.style.display = 'flex';
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
overflow.style.display = 'none';
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
defineComponent('nc-avatar-group', NcAvatarGroup);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Component } from '../core/component.js';
|
|
2
|
+
export declare class NcAvatar extends Component {
|
|
3
|
+
static useShadowDOM: boolean;
|
|
4
|
+
static get observedAttributes(): string[];
|
|
5
|
+
private imageError;
|
|
6
|
+
template(): string;
|
|
7
|
+
onMount(): void;
|
|
8
|
+
attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Component, defineComponent } from '../core/component.js';
|
|
2
|
+
const SIZE_MAP = {
|
|
3
|
+
xs: '24px',
|
|
4
|
+
sm: '32px',
|
|
5
|
+
md: '40px',
|
|
6
|
+
lg: '48px',
|
|
7
|
+
xl: '64px',
|
|
8
|
+
'2xl': '80px'
|
|
9
|
+
};
|
|
10
|
+
const STATUS_COLORS = {
|
|
11
|
+
online: '#22c55e',
|
|
12
|
+
offline: '#94a3b8',
|
|
13
|
+
away: '#f59e0b',
|
|
14
|
+
busy: '#ef4444'
|
|
15
|
+
};
|
|
16
|
+
function initials(name) {
|
|
17
|
+
return name.trim().split(/\s+/).map(word => word[0] ?? '').join('').toUpperCase().slice(0, 2);
|
|
18
|
+
}
|
|
19
|
+
export class NcAvatar extends Component {
|
|
20
|
+
static useShadowDOM = true;
|
|
21
|
+
static get observedAttributes() {
|
|
22
|
+
return ['src', 'alt', 'size', 'shape', 'variant', 'status', 'status-position'];
|
|
23
|
+
}
|
|
24
|
+
imageError = false;
|
|
25
|
+
template() {
|
|
26
|
+
const src = this.getAttribute('src') || '';
|
|
27
|
+
const alt = this.getAttribute('alt') || '';
|
|
28
|
+
const sizeAttr = this.getAttribute('size') || 'md';
|
|
29
|
+
const shape = this.getAttribute('shape') || 'circle';
|
|
30
|
+
const variant = this.getAttribute('variant') || 'neutral';
|
|
31
|
+
const status = this.getAttribute('status') || '';
|
|
32
|
+
const statusPosition = this.getAttribute('status-position') || 'bottom-right';
|
|
33
|
+
const sizeValue = SIZE_MAP[sizeAttr] ?? sizeAttr;
|
|
34
|
+
const fontSize = `calc(${sizeValue} * 0.38)`;
|
|
35
|
+
const statusSize = `calc(${sizeValue} * 0.26)`;
|
|
36
|
+
const borderRadius = shape === 'circle' ? '50%' : shape === 'rounded' ? '25%' : '8px';
|
|
37
|
+
const letters = initials(alt);
|
|
38
|
+
const showImage = src && !this.imageError;
|
|
39
|
+
const [verticalPosition, horizontalPosition] = statusPosition.split('-');
|
|
40
|
+
const statusDotStyle = [
|
|
41
|
+
verticalPosition === 'top' ? 'top: 0;' : 'bottom: 0;',
|
|
42
|
+
horizontalPosition === 'right' ? 'right: 0;' : 'left: 0;'
|
|
43
|
+
].join(' ');
|
|
44
|
+
const variantBackground = {
|
|
45
|
+
primary: 'var(--nc-primary, #10b981)',
|
|
46
|
+
secondary: 'var(--nc-secondary, #6366f1)',
|
|
47
|
+
success: 'var(--nc-success, #10b981)',
|
|
48
|
+
warning: 'var(--nc-warning, #f59e0b)',
|
|
49
|
+
danger: 'var(--nc-danger, #ef4444)',
|
|
50
|
+
neutral: 'var(--nc-bg-tertiary, #e5e7eb)'
|
|
51
|
+
};
|
|
52
|
+
return `
|
|
53
|
+
<style>
|
|
54
|
+
:host { display: inline-flex; position: relative; flex-shrink: 0; }
|
|
55
|
+
|
|
56
|
+
.avatar {
|
|
57
|
+
width: ${sizeValue};
|
|
58
|
+
height: ${sizeValue};
|
|
59
|
+
border-radius: ${borderRadius};
|
|
60
|
+
overflow: hidden;
|
|
61
|
+
display: flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
justify-content: center;
|
|
64
|
+
background: ${variantBackground[variant] ?? variantBackground.neutral};
|
|
65
|
+
color: ${variant === 'neutral' ? 'var(--nc-text, #111827)' : '#fff'};
|
|
66
|
+
font-family: var(--nc-font-family);
|
|
67
|
+
font-size: ${fontSize};
|
|
68
|
+
font-weight: var(--nc-font-weight-semibold, 600);
|
|
69
|
+
user-select: none;
|
|
70
|
+
flex-shrink: 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
img {
|
|
74
|
+
width: 100%;
|
|
75
|
+
height: 100%;
|
|
76
|
+
object-fit: cover;
|
|
77
|
+
display: ${showImage ? 'block' : 'none'};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.initials {
|
|
81
|
+
display: ${showImage ? 'none' : 'flex'};
|
|
82
|
+
align-items: center;
|
|
83
|
+
justify-content: center;
|
|
84
|
+
width: 100%;
|
|
85
|
+
height: 100%;
|
|
86
|
+
line-height: 1;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.status-dot {
|
|
90
|
+
position: absolute;
|
|
91
|
+
${statusDotStyle}
|
|
92
|
+
width: ${statusSize};
|
|
93
|
+
height: ${statusSize};
|
|
94
|
+
border-radius: 50%;
|
|
95
|
+
background: ${STATUS_COLORS[status] ?? STATUS_COLORS.offline};
|
|
96
|
+
border: 2px solid var(--nc-bg, #ffffff);
|
|
97
|
+
display: ${status ? 'block' : 'none'};
|
|
98
|
+
}
|
|
99
|
+
</style>
|
|
100
|
+
<div class="avatar" title="${alt}" aria-label="${alt}" role="img">
|
|
101
|
+
${showImage ? `<img src="${src}" alt="${alt}" />` : ''}
|
|
102
|
+
<span class="initials">${letters || '?'}</span>
|
|
103
|
+
</div>
|
|
104
|
+
${status ? `<span class="status-dot" aria-label="${status}"></span>` : ''}
|
|
105
|
+
`;
|
|
106
|
+
}
|
|
107
|
+
onMount() {
|
|
108
|
+
const image = this.$('img');
|
|
109
|
+
if (image) {
|
|
110
|
+
image.addEventListener('error', () => {
|
|
111
|
+
this.imageError = true;
|
|
112
|
+
this.render();
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
117
|
+
if (oldValue !== newValue) {
|
|
118
|
+
if (name === 'src')
|
|
119
|
+
this.imageError = false;
|
|
120
|
+
if (this._mounted) {
|
|
121
|
+
this.render();
|
|
122
|
+
this.onMount();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
defineComponent('nc-avatar', NcAvatar);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Component } from '../core/component.js';
|
|
2
|
+
export declare class NcBadge 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,63 @@
|
|
|
1
|
+
import { Component, defineComponent } from '../core/component.js';
|
|
2
|
+
export class NcBadge extends Component {
|
|
3
|
+
static useShadowDOM = true;
|
|
4
|
+
static get observedAttributes() {
|
|
5
|
+
return ['count', 'max', 'show-zero', 'dot', 'variant', 'position'];
|
|
6
|
+
}
|
|
7
|
+
template() {
|
|
8
|
+
const count = Number(this.getAttribute('count') || 0);
|
|
9
|
+
const max = Number(this.getAttribute('max') || 99);
|
|
10
|
+
const showZero = this.hasAttribute('show-zero');
|
|
11
|
+
const dot = this.hasAttribute('dot');
|
|
12
|
+
const variant = this.getAttribute('variant') || 'danger';
|
|
13
|
+
const position = this.getAttribute('position') || 'top-right';
|
|
14
|
+
const visible = dot || showZero || count > 0;
|
|
15
|
+
const label = dot ? '' : count > max ? `${max}+` : String(count);
|
|
16
|
+
const [verticalPosition, horizontalPosition] = position.split('-');
|
|
17
|
+
return `
|
|
18
|
+
<style>
|
|
19
|
+
:host { display: inline-flex; position: relative; vertical-align: middle; }
|
|
20
|
+
|
|
21
|
+
.badge {
|
|
22
|
+
position: absolute;
|
|
23
|
+
${verticalPosition === 'top' ? 'top: -6px;' : 'bottom: -6px;'}
|
|
24
|
+
${horizontalPosition === 'right' ? 'right: -6px;' : 'left: -6px;'}
|
|
25
|
+
z-index: 1;
|
|
26
|
+
display: ${visible ? 'inline-flex' : 'none'};
|
|
27
|
+
align-items: center;
|
|
28
|
+
justify-content: center;
|
|
29
|
+
font-family: var(--nc-font-family);
|
|
30
|
+
font-size: 0.65rem;
|
|
31
|
+
font-weight: var(--nc-font-weight-bold);
|
|
32
|
+
line-height: 1;
|
|
33
|
+
min-width: ${dot ? '8px' : '18px'};
|
|
34
|
+
height: ${dot ? '8px' : '18px'};
|
|
35
|
+
padding: ${dot ? '0' : '0 5px'};
|
|
36
|
+
border-radius: 999px;
|
|
37
|
+
border: 2px solid var(--nc-bg, #ffffff);
|
|
38
|
+
white-space: nowrap;
|
|
39
|
+
pointer-events: none;
|
|
40
|
+
transition: transform var(--nc-transition-fast, 160ms ease);
|
|
41
|
+
transform: scale(${visible ? '1' : '0'});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.badge--primary { background: var(--nc-primary, #10b981); color: #fff; }
|
|
45
|
+
.badge--secondary { background: var(--nc-secondary, #6366f1); color: #fff; }
|
|
46
|
+
.badge--danger { background: var(--nc-danger, #ef4444); color: #fff; }
|
|
47
|
+
.badge--warning { background: var(--nc-warning, #f59e0b); color: #fff; }
|
|
48
|
+
.badge--success { background: var(--nc-success, #10b981); color: #fff; }
|
|
49
|
+
.badge--info { background: var(--nc-info, #3b82f6); color: #fff; }
|
|
50
|
+
.badge--neutral { background: var(--nc-text-muted, #6b7280); color: #fff; }
|
|
51
|
+
|
|
52
|
+
::slotted(*) { display: inline-flex; }
|
|
53
|
+
</style>
|
|
54
|
+
<slot></slot>
|
|
55
|
+
<span class="badge badge--${variant}" aria-label="${dot ? 'indicator' : `${label} notifications`}">${label}</span>
|
|
56
|
+
`;
|
|
57
|
+
}
|
|
58
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
59
|
+
if (oldValue !== newValue && this._mounted)
|
|
60
|
+
this.render();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
defineComponent('nc-badge', NcBadge);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NcBottomNav Component - mobile bottom navigation bar
|
|
3
|
+
*
|
|
4
|
+
* Container for nc-nav-bottom-item children. Handles active state management.
|
|
5
|
+
* Designed for mobile viewports but works on all sizes.
|
|
6
|
+
*
|
|
7
|
+
* Attributes:
|
|
8
|
+
* value - currently active tab value
|
|
9
|
+
* variant - 'default'|'labeled'|'icon-only' (default: 'labeled')
|
|
10
|
+
* elevated - boolean - add drop shadow / elevated appearance
|
|
11
|
+
* bordered - boolean - top border (default: true)
|
|
12
|
+
*
|
|
13
|
+
* Slots:
|
|
14
|
+
* (default) - nc-bottom-nav-item elements
|
|
15
|
+
*
|
|
16
|
+
* Events:
|
|
17
|
+
* change - CustomEvent<{ value: string }> - tab changed
|
|
18
|
+
*
|
|
19
|
+
* Usage:
|
|
20
|
+
* <nc-bottom-nav value="home">
|
|
21
|
+
* <nc-bottom-nav-item value="home" icon="home" label="Home"></nc-bottom-nav-item>
|
|
22
|
+
* <nc-bottom-nav-item value="search" icon="search" label="Search"></nc-bottom-nav-item>
|
|
23
|
+
* <nc-bottom-nav-item value="inbox" icon="inbox" label="Inbox" badge="3"></nc-bottom-nav-item>
|
|
24
|
+
* <nc-bottom-nav-item value="me" icon="users" label="Me"></nc-bottom-nav-item>
|
|
25
|
+
* </nc-bottom-nav>
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* NcBottomNavItem Component - individual tab in a bottom nav bar
|
|
29
|
+
*
|
|
30
|
+
* Attributes:
|
|
31
|
+
* value - unique identifier for this tab
|
|
32
|
+
* label - tab label text
|
|
33
|
+
* icon - icon name (same set as nc-nav-item)
|
|
34
|
+
* badge - numeric badge count
|
|
35
|
+
* disabled - boolean
|
|
36
|
+
* active - boolean (managed by parent nc-bottom-nav)
|
|
37
|
+
*/
|
|
38
|
+
import { Component } from '../core/component.js';
|
|
39
|
+
export declare class NcBottomNavItem extends Component {
|
|
40
|
+
static useShadowDOM: boolean;
|
|
41
|
+
static get observedAttributes(): string[];
|
|
42
|
+
template(): string;
|
|
43
|
+
onMount(): void;
|
|
44
|
+
attributeChangedCallback(n: string, o: string, v: string): void;
|
|
45
|
+
}
|
|
46
|
+
export declare class NcBottomNav extends Component {
|
|
47
|
+
static useShadowDOM: boolean;
|
|
48
|
+
static get observedAttributes(): string[];
|
|
49
|
+
template(): string;
|
|
50
|
+
onMount(): void;
|
|
51
|
+
private _setActive;
|
|
52
|
+
attributeChangedCallback(n: string, o: string, v: string): void;
|
|
53
|
+
}
|