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
|
+
* NcSlider Component
|
|
3
|
+
*
|
|
4
|
+
* NativeCore Framework Core Component
|
|
5
|
+
*
|
|
6
|
+
* Attributes:
|
|
7
|
+
* - name: string - form field name
|
|
8
|
+
* - value: number - current value (default: min)
|
|
9
|
+
* - min: number - minimum value (default: 0)
|
|
10
|
+
* - max: number - maximum value (default: 100)
|
|
11
|
+
* - step: number - step increment (default: 1)
|
|
12
|
+
* - disabled: boolean - disabled state
|
|
13
|
+
* - show-value: boolean - show current value bubble above thumb
|
|
14
|
+
* - size: 'sm' | 'md' | 'lg' (default: 'md')
|
|
15
|
+
* - variant: 'primary' | 'success' | 'danger' (default: 'primary')
|
|
16
|
+
*
|
|
17
|
+
* Events:
|
|
18
|
+
* - change: CustomEvent<{ value: number; name: string }>
|
|
19
|
+
* - input: CustomEvent<{ value: number; name: string }>
|
|
20
|
+
*
|
|
21
|
+
* Usage:
|
|
22
|
+
* <nc-slider name="volume" min="0" max="100" value="50" show-value></nc-slider>
|
|
23
|
+
* <nc-slider name="opacity" min="0" max="1" step="0.01" value="0.5"></nc-slider>
|
|
24
|
+
*/
|
|
25
|
+
import { Component } from '../core/component.js';
|
|
26
|
+
export declare class NcSlider extends Component {
|
|
27
|
+
static useShadowDOM: boolean;
|
|
28
|
+
static attributeOptions: {
|
|
29
|
+
variant: string[];
|
|
30
|
+
size: string[];
|
|
31
|
+
};
|
|
32
|
+
static get observedAttributes(): string[];
|
|
33
|
+
constructor();
|
|
34
|
+
private _getNum;
|
|
35
|
+
template(): string;
|
|
36
|
+
onMount(): void;
|
|
37
|
+
private _updateLive;
|
|
38
|
+
private _bindEvents;
|
|
39
|
+
private _isDragging;
|
|
40
|
+
attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
|
|
41
|
+
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NcSlider Component
|
|
3
|
+
*
|
|
4
|
+
* NativeCore Framework Core Component
|
|
5
|
+
*
|
|
6
|
+
* Attributes:
|
|
7
|
+
* - name: string - form field name
|
|
8
|
+
* - value: number - current value (default: min)
|
|
9
|
+
* - min: number - minimum value (default: 0)
|
|
10
|
+
* - max: number - maximum value (default: 100)
|
|
11
|
+
* - step: number - step increment (default: 1)
|
|
12
|
+
* - disabled: boolean - disabled state
|
|
13
|
+
* - show-value: boolean - show current value bubble above thumb
|
|
14
|
+
* - size: 'sm' | 'md' | 'lg' (default: 'md')
|
|
15
|
+
* - variant: 'primary' | 'success' | 'danger' (default: 'primary')
|
|
16
|
+
*
|
|
17
|
+
* Events:
|
|
18
|
+
* - change: CustomEvent<{ value: number; name: string }>
|
|
19
|
+
* - input: CustomEvent<{ value: number; name: string }>
|
|
20
|
+
*
|
|
21
|
+
* Usage:
|
|
22
|
+
* <nc-slider name="volume" min="0" max="100" value="50" show-value></nc-slider>
|
|
23
|
+
* <nc-slider name="opacity" min="0" max="1" step="0.01" value="0.5"></nc-slider>
|
|
24
|
+
*/
|
|
25
|
+
import { Component, defineComponent } from '../core/component.js';
|
|
26
|
+
export class NcSlider extends Component {
|
|
27
|
+
static useShadowDOM = true;
|
|
28
|
+
static attributeOptions = {
|
|
29
|
+
variant: ['primary', 'success', 'danger'],
|
|
30
|
+
size: ['sm', 'md', 'lg']
|
|
31
|
+
};
|
|
32
|
+
static get observedAttributes() {
|
|
33
|
+
return ['name', 'value', 'min', 'max', 'step', 'disabled', 'show-value', 'size', 'variant'];
|
|
34
|
+
}
|
|
35
|
+
constructor() {
|
|
36
|
+
super();
|
|
37
|
+
}
|
|
38
|
+
_getNum(attr, fallback) {
|
|
39
|
+
const v = this.getAttribute(attr);
|
|
40
|
+
return v !== null && v !== '' ? Number(v) : fallback;
|
|
41
|
+
}
|
|
42
|
+
template() {
|
|
43
|
+
const min = this._getNum('min', 0);
|
|
44
|
+
const max = this._getNum('max', 100);
|
|
45
|
+
const step = this._getNum('step', 1);
|
|
46
|
+
const value = this._getNum('value', min);
|
|
47
|
+
const disabled = this.hasAttribute('disabled');
|
|
48
|
+
const showValue = this.hasAttribute('show-value');
|
|
49
|
+
const pct = max === min ? 0 : ((value - min) / (max - min)) * 100;
|
|
50
|
+
// Set fill % as a host CSS variable so it can be updated without re-render
|
|
51
|
+
this.style.setProperty('--_fill-pct', `${pct}%`);
|
|
52
|
+
return `
|
|
53
|
+
<style>
|
|
54
|
+
:host {
|
|
55
|
+
display: block;
|
|
56
|
+
font-family: var(--nc-font-family);
|
|
57
|
+
width: 100%;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.slider-wrap {
|
|
61
|
+
position: relative;
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
width: 100%;
|
|
65
|
+
padding-top: ${showValue ? '1.75rem' : '0'};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
input[type="range"] {
|
|
69
|
+
-webkit-appearance: none;
|
|
70
|
+
appearance: none;
|
|
71
|
+
width: 100%;
|
|
72
|
+
background: transparent;
|
|
73
|
+
cursor: ${disabled ? 'not-allowed' : 'pointer'};
|
|
74
|
+
outline: none;
|
|
75
|
+
margin: 0;
|
|
76
|
+
opacity: ${disabled ? '0.5' : '1'};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* Track - WebKit: uses the host CSS variable for live fill */
|
|
80
|
+
input[type="range"]::-webkit-slider-runnable-track {
|
|
81
|
+
height: var(--track-h);
|
|
82
|
+
border-radius: var(--nc-radius-full);
|
|
83
|
+
background: linear-gradient(
|
|
84
|
+
to right,
|
|
85
|
+
var(--track-fill) 0%,
|
|
86
|
+
var(--track-fill) var(--_fill-pct, ${pct}%),
|
|
87
|
+
var(--nc-gray-200) var(--_fill-pct, ${pct}%),
|
|
88
|
+
var(--nc-gray-200) 100%
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Track - Firefox */
|
|
93
|
+
input[type="range"]::-moz-range-track {
|
|
94
|
+
height: var(--track-h);
|
|
95
|
+
border-radius: var(--nc-radius-full);
|
|
96
|
+
background: var(--nc-gray-200);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
input[type="range"]::-moz-range-progress {
|
|
100
|
+
height: var(--track-h);
|
|
101
|
+
border-radius: var(--nc-radius-full);
|
|
102
|
+
background: var(--track-fill);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Thumb - WebKit */
|
|
106
|
+
input[type="range"]::-webkit-slider-thumb {
|
|
107
|
+
-webkit-appearance: none;
|
|
108
|
+
appearance: none;
|
|
109
|
+
width: var(--thumb-size);
|
|
110
|
+
height: var(--thumb-size);
|
|
111
|
+
border-radius: var(--nc-radius-full);
|
|
112
|
+
background: var(--nc-white);
|
|
113
|
+
border: 2px solid var(--track-fill);
|
|
114
|
+
box-shadow: var(--nc-shadow-sm);
|
|
115
|
+
margin-top: calc((var(--track-h) - var(--thumb-size)) / 2);
|
|
116
|
+
transition: box-shadow var(--nc-transition-fast), transform var(--nc-transition-fast);
|
|
117
|
+
cursor: ${disabled ? 'not-allowed' : 'pointer'};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
input[type="range"]::-webkit-slider-thumb:hover {
|
|
121
|
+
box-shadow: 0 0 0 6px color-mix(in srgb, var(--track-fill) 15%, transparent);
|
|
122
|
+
transform: scale(1.1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
input[type="range"]::-webkit-slider-thumb:active {
|
|
126
|
+
transform: scale(1.15);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* Thumb - Firefox */
|
|
130
|
+
input[type="range"]::-moz-range-thumb {
|
|
131
|
+
width: var(--thumb-size);
|
|
132
|
+
height: var(--thumb-size);
|
|
133
|
+
border-radius: var(--nc-radius-full);
|
|
134
|
+
background: var(--nc-white);
|
|
135
|
+
border: 2px solid var(--track-fill);
|
|
136
|
+
box-shadow: var(--nc-shadow-sm);
|
|
137
|
+
cursor: ${disabled ? 'not-allowed' : 'pointer'};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
input[type="range"]:focus-visible::-webkit-slider-thumb {
|
|
141
|
+
box-shadow: 0 0 0 3px var(--nc-bg), 0 0 0 5px var(--track-fill);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* Size tokens */
|
|
145
|
+
:host([size="sm"]),
|
|
146
|
+
:host(:not([size])) {
|
|
147
|
+
--track-h: 3px;
|
|
148
|
+
--thumb-size: 14px;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
:host([size="md"]) {
|
|
152
|
+
--track-h: 4px;
|
|
153
|
+
--thumb-size: 18px;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
:host([size="lg"]) {
|
|
157
|
+
--track-h: 6px;
|
|
158
|
+
--thumb-size: 22px;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
:host {
|
|
162
|
+
--track-h: 4px;
|
|
163
|
+
--thumb-size: 18px;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
:host,
|
|
167
|
+
:host([variant="primary"]) { --track-fill: var(--nc-primary); }
|
|
168
|
+
:host([variant="success"]) { --track-fill: var(--nc-success); }
|
|
169
|
+
:host([variant="danger"]) { --track-fill: var(--nc-danger); }
|
|
170
|
+
|
|
171
|
+
/* Value bubble - position driven by --_fill-pct */
|
|
172
|
+
.value-bubble {
|
|
173
|
+
position: absolute;
|
|
174
|
+
top: 0;
|
|
175
|
+
left: var(--_fill-pct, ${pct}%);
|
|
176
|
+
transform: translateX(-50%);
|
|
177
|
+
background: var(--track-fill);
|
|
178
|
+
color: var(--nc-white);
|
|
179
|
+
font-size: var(--nc-font-size-xs);
|
|
180
|
+
font-weight: var(--nc-font-weight-semibold);
|
|
181
|
+
padding: 1px 6px;
|
|
182
|
+
border-radius: var(--nc-radius-sm);
|
|
183
|
+
white-space: nowrap;
|
|
184
|
+
pointer-events: none;
|
|
185
|
+
line-height: 1.5;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.value-bubble::after {
|
|
189
|
+
content: '';
|
|
190
|
+
position: absolute;
|
|
191
|
+
top: 100%;
|
|
192
|
+
left: 50%;
|
|
193
|
+
transform: translateX(-50%);
|
|
194
|
+
border: 4px solid transparent;
|
|
195
|
+
border-top-color: var(--track-fill);
|
|
196
|
+
}
|
|
197
|
+
</style>
|
|
198
|
+
|
|
199
|
+
<div class="slider-wrap">
|
|
200
|
+
${showValue ? `<span class="value-bubble">${value}</span>` : ''}
|
|
201
|
+
<input
|
|
202
|
+
type="range"
|
|
203
|
+
min="${min}"
|
|
204
|
+
max="${max}"
|
|
205
|
+
step="${step}"
|
|
206
|
+
value="${value}"
|
|
207
|
+
${disabled ? 'disabled' : ''}
|
|
208
|
+
name="${this.getAttribute('name') || ''}"
|
|
209
|
+
aria-valuemin="${min}"
|
|
210
|
+
aria-valuemax="${max}"
|
|
211
|
+
aria-valuenow="${value}"
|
|
212
|
+
/>
|
|
213
|
+
</div>
|
|
214
|
+
`;
|
|
215
|
+
}
|
|
216
|
+
onMount() {
|
|
217
|
+
this._bindEvents();
|
|
218
|
+
}
|
|
219
|
+
_updateLive(val) {
|
|
220
|
+
const min = this._getNum('min', 0);
|
|
221
|
+
const max = this._getNum('max', 100);
|
|
222
|
+
const pct = max === min ? 0 : ((val - min) / (max - min)) * 100;
|
|
223
|
+
// Update CSS var - drives both the track gradient and bubble position
|
|
224
|
+
this.style.setProperty('--_fill-pct', `${pct}%`);
|
|
225
|
+
// Update bubble text without touching the shadow DOM structure
|
|
226
|
+
const bubble = this.shadowRoot.querySelector('.value-bubble');
|
|
227
|
+
if (bubble)
|
|
228
|
+
bubble.textContent = String(val);
|
|
229
|
+
}
|
|
230
|
+
_bindEvents() {
|
|
231
|
+
const input = this.shadowRoot.querySelector('input[type="range"]');
|
|
232
|
+
if (!input)
|
|
233
|
+
return;
|
|
234
|
+
input.addEventListener('input', () => {
|
|
235
|
+
const val = Number(input.value);
|
|
236
|
+
this._updateLive(val);
|
|
237
|
+
this.dispatchEvent(new CustomEvent('input', {
|
|
238
|
+
bubbles: true,
|
|
239
|
+
composed: true,
|
|
240
|
+
detail: { value: val, name: this.getAttribute('name') || '' }
|
|
241
|
+
}));
|
|
242
|
+
});
|
|
243
|
+
input.addEventListener('change', () => {
|
|
244
|
+
const val = Number(input.value);
|
|
245
|
+
// Safe to commit after drag is done - no longer mid-interaction
|
|
246
|
+
this._isDragging = false;
|
|
247
|
+
this.setAttribute('value', String(val));
|
|
248
|
+
this.dispatchEvent(new CustomEvent('change', {
|
|
249
|
+
bubbles: true,
|
|
250
|
+
composed: true,
|
|
251
|
+
detail: { value: val, name: this.getAttribute('name') || '' }
|
|
252
|
+
}));
|
|
253
|
+
});
|
|
254
|
+
input.addEventListener('mousedown', () => { this._isDragging = true; });
|
|
255
|
+
input.addEventListener('touchstart', () => { this._isDragging = true; });
|
|
256
|
+
}
|
|
257
|
+
_isDragging = false;
|
|
258
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
259
|
+
if (oldValue === newValue)
|
|
260
|
+
return;
|
|
261
|
+
// Never re-render while dragging or on value-only changes (live updates handle those)
|
|
262
|
+
if (name === 'value' && this._isDragging)
|
|
263
|
+
return;
|
|
264
|
+
this.render();
|
|
265
|
+
this._bindEvents();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
defineComponent('nc-slider', NcSlider);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NcSnackbar Component
|
|
3
|
+
*
|
|
4
|
+
* A singleton toast notification manager. Attach once to the app shell;
|
|
5
|
+
* dispatch 'nc-toast' events from anywhere to show notifications.
|
|
6
|
+
*
|
|
7
|
+
* Attributes:
|
|
8
|
+
* - position: 'top-right'|'top-center'|'top-left'|'bottom-right'|'bottom-center'|'bottom-left'
|
|
9
|
+
* (default: 'bottom-right')
|
|
10
|
+
* - max: number - max visible toasts (default: 5)
|
|
11
|
+
*
|
|
12
|
+
* Programmatic API (after import):
|
|
13
|
+
* NcSnackbar.show({ message, variant?, duration?, dismissible? })
|
|
14
|
+
*
|
|
15
|
+
* Event-based API (no import needed):
|
|
16
|
+
* document.dispatchEvent(new CustomEvent('nc-toast', {
|
|
17
|
+
* detail: { message: 'Saved!', variant: 'success', duration: 3000 }
|
|
18
|
+
* }));
|
|
19
|
+
*
|
|
20
|
+
* Toast options:
|
|
21
|
+
* - message: string
|
|
22
|
+
* - variant: 'default'|'success'|'warning'|'danger'|'info' (default: 'default')
|
|
23
|
+
* - duration: number - ms before auto-dismiss (default: 4000, 0 = sticky)
|
|
24
|
+
* - dismissible: boolean - show close button (default: true)
|
|
25
|
+
* - icon: boolean - show variant icon (default: true)
|
|
26
|
+
*/
|
|
27
|
+
import { Component } from '../core/component.js';
|
|
28
|
+
interface ToastOptions {
|
|
29
|
+
message: string;
|
|
30
|
+
variant?: string;
|
|
31
|
+
duration?: number;
|
|
32
|
+
dismissible?: boolean;
|
|
33
|
+
icon?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare class NcSnackbar extends Component {
|
|
36
|
+
static useShadowDOM: boolean;
|
|
37
|
+
private _toasts;
|
|
38
|
+
private _timers;
|
|
39
|
+
static get observedAttributes(): string[];
|
|
40
|
+
static show(opts: ToastOptions): void;
|
|
41
|
+
template(): string;
|
|
42
|
+
private _renderToast;
|
|
43
|
+
onMount(): void;
|
|
44
|
+
private _bindEvents;
|
|
45
|
+
_add(opts: ToastOptions): void;
|
|
46
|
+
private _dismiss;
|
|
47
|
+
private _clearTimer;
|
|
48
|
+
onUnmount(): void;
|
|
49
|
+
attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
|
|
50
|
+
}
|
|
51
|
+
export {};
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NcSnackbar Component
|
|
3
|
+
*
|
|
4
|
+
* A singleton toast notification manager. Attach once to the app shell;
|
|
5
|
+
* dispatch 'nc-toast' events from anywhere to show notifications.
|
|
6
|
+
*
|
|
7
|
+
* Attributes:
|
|
8
|
+
* - position: 'top-right'|'top-center'|'top-left'|'bottom-right'|'bottom-center'|'bottom-left'
|
|
9
|
+
* (default: 'bottom-right')
|
|
10
|
+
* - max: number - max visible toasts (default: 5)
|
|
11
|
+
*
|
|
12
|
+
* Programmatic API (after import):
|
|
13
|
+
* NcSnackbar.show({ message, variant?, duration?, dismissible? })
|
|
14
|
+
*
|
|
15
|
+
* Event-based API (no import needed):
|
|
16
|
+
* document.dispatchEvent(new CustomEvent('nc-toast', {
|
|
17
|
+
* detail: { message: 'Saved!', variant: 'success', duration: 3000 }
|
|
18
|
+
* }));
|
|
19
|
+
*
|
|
20
|
+
* Toast options:
|
|
21
|
+
* - message: string
|
|
22
|
+
* - variant: 'default'|'success'|'warning'|'danger'|'info' (default: 'default')
|
|
23
|
+
* - duration: number - ms before auto-dismiss (default: 4000, 0 = sticky)
|
|
24
|
+
* - dismissible: boolean - show close button (default: true)
|
|
25
|
+
* - icon: boolean - show variant icon (default: true)
|
|
26
|
+
*/
|
|
27
|
+
import { Component, defineComponent } from '../core/component.js';
|
|
28
|
+
import { dom } from '../utils/dom.js';
|
|
29
|
+
const ICONS = {
|
|
30
|
+
success: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="16" height="16"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/></svg>`,
|
|
31
|
+
danger: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="16" height="16"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/></svg>`,
|
|
32
|
+
warning: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="16" height="16"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/></svg>`,
|
|
33
|
+
info: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="16" height="16"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/></svg>`,
|
|
34
|
+
default: '',
|
|
35
|
+
};
|
|
36
|
+
let _seq = 0;
|
|
37
|
+
export class NcSnackbar extends Component {
|
|
38
|
+
static useShadowDOM = true;
|
|
39
|
+
_toasts = [];
|
|
40
|
+
_timers = new Map();
|
|
41
|
+
static get observedAttributes() { return ['position', 'max']; }
|
|
42
|
+
// Static helper so other modules can call NcSnackbar.show({...})
|
|
43
|
+
static show(opts) {
|
|
44
|
+
const el = dom.query('nc-snackbar');
|
|
45
|
+
el?._add(opts);
|
|
46
|
+
}
|
|
47
|
+
template() {
|
|
48
|
+
const position = this.getAttribute('position') || 'bottom-right';
|
|
49
|
+
const [vPos, hPos] = position.split('-');
|
|
50
|
+
return `
|
|
51
|
+
<style>
|
|
52
|
+
:host {
|
|
53
|
+
position: fixed;
|
|
54
|
+
${vPos === 'top' ? 'top: var(--nc-spacing-lg, 1.5rem);' : 'bottom: var(--nc-spacing-lg, 1.5rem);'}
|
|
55
|
+
${hPos === 'right' ? 'right: var(--nc-spacing-lg, 1.5rem);' : hPos === 'left' ? 'left: var(--nc-spacing-lg, 1.5rem);' : 'left: 50%; transform: translateX(-50%);'}
|
|
56
|
+
z-index: 10000;
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: ${vPos === 'top' ? 'column' : 'column-reverse'};
|
|
59
|
+
gap: var(--nc-spacing-sm);
|
|
60
|
+
pointer-events: none;
|
|
61
|
+
max-width: min(380px, calc(100vw - 2rem));
|
|
62
|
+
width: max-content;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.toast {
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: flex-start;
|
|
68
|
+
gap: var(--nc-spacing-sm);
|
|
69
|
+
padding: var(--nc-spacing-sm) var(--nc-spacing-md);
|
|
70
|
+
border-radius: var(--nc-radius-md, 8px);
|
|
71
|
+
box-shadow: var(--nc-shadow-lg);
|
|
72
|
+
font-family: var(--nc-font-family);
|
|
73
|
+
font-size: var(--nc-font-size-sm);
|
|
74
|
+
pointer-events: all;
|
|
75
|
+
animation: nc-toast-in 0.22s ease forwards;
|
|
76
|
+
min-width: 240px;
|
|
77
|
+
max-width: 380px;
|
|
78
|
+
border: 1px solid transparent;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.toast.out { animation: nc-toast-out 0.18s ease forwards; }
|
|
82
|
+
|
|
83
|
+
@keyframes nc-toast-in {
|
|
84
|
+
from { opacity: 0; transform: ${vPos === 'top' ? 'translateY(-8px)' : 'translateY(8px)'}; }
|
|
85
|
+
to { opacity: 1; transform: translateY(0); }
|
|
86
|
+
}
|
|
87
|
+
@keyframes nc-toast-out {
|
|
88
|
+
to { opacity: 0; transform: scale(0.95); }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.toast--default { background: var(--nc-bg); color: var(--nc-text); border-color: var(--nc-border); }
|
|
92
|
+
.toast--success { background: #f0fdf4; color: #166534; border-color: #86efac; }
|
|
93
|
+
.toast--danger { background: #fef2f2; color: #991b1b; border-color: #fca5a5; }
|
|
94
|
+
.toast--warning { background: #fffbeb; color: #92400e; border-color: #fcd34d; }
|
|
95
|
+
.toast--info { background: #eff6ff; color: #1e40af; border-color: #93c5fd; }
|
|
96
|
+
|
|
97
|
+
.toast__icon { flex-shrink: 0; margin-top: 1px; }
|
|
98
|
+
.toast__msg { flex: 1; line-height: 1.5; }
|
|
99
|
+
|
|
100
|
+
.toast__close {
|
|
101
|
+
background: none;
|
|
102
|
+
border: none;
|
|
103
|
+
cursor: pointer;
|
|
104
|
+
padding: 0;
|
|
105
|
+
color: inherit;
|
|
106
|
+
opacity: 0.5;
|
|
107
|
+
flex-shrink: 0;
|
|
108
|
+
line-height: 1;
|
|
109
|
+
transition: opacity var(--nc-transition-fast);
|
|
110
|
+
}
|
|
111
|
+
.toast__close:hover { opacity: 1; }
|
|
112
|
+
</style>
|
|
113
|
+
${this._toasts.map(t => this._renderToast(t)).join('')}
|
|
114
|
+
`;
|
|
115
|
+
}
|
|
116
|
+
_renderToast(t) {
|
|
117
|
+
const icon = ICONS[t.variant] || '';
|
|
118
|
+
return `
|
|
119
|
+
<div class="toast toast--${t.variant}" data-id="${t.id}" role="alert" aria-live="polite">
|
|
120
|
+
${icon && t.icon ? `<span class="toast__icon">${icon}</span>` : ''}
|
|
121
|
+
<span class="toast__msg">${t.message}</span>
|
|
122
|
+
${t.dismissible ? `
|
|
123
|
+
<button class="toast__close" data-dismiss="${t.id}" type="button" aria-label="Dismiss">
|
|
124
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" fill="none" width="12" height="12">
|
|
125
|
+
<path d="M2 2l8 8M10 2l-8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
126
|
+
</svg>
|
|
127
|
+
</button>` : ''}
|
|
128
|
+
</div>`;
|
|
129
|
+
}
|
|
130
|
+
onMount() {
|
|
131
|
+
this._bindEvents();
|
|
132
|
+
// Global event listener
|
|
133
|
+
document.addEventListener('nc-toast', (e) => {
|
|
134
|
+
this._add(e.detail);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
_bindEvents() {
|
|
138
|
+
this.shadowRoot.addEventListener('click', (e) => {
|
|
139
|
+
const btn = e.target.closest('[data-dismiss]');
|
|
140
|
+
if (btn) {
|
|
141
|
+
const id = Number(btn.dataset.dismiss);
|
|
142
|
+
this._dismiss(id);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
_add(opts) {
|
|
147
|
+
const max = Number(this.getAttribute('max') || 5);
|
|
148
|
+
const toast = {
|
|
149
|
+
id: ++_seq,
|
|
150
|
+
message: opts.message,
|
|
151
|
+
variant: opts.variant || 'default',
|
|
152
|
+
duration: opts.duration ?? 4000,
|
|
153
|
+
dismissible: opts.dismissible ?? true,
|
|
154
|
+
icon: opts.icon ?? true,
|
|
155
|
+
};
|
|
156
|
+
this._toasts.push(toast);
|
|
157
|
+
// Cap to max
|
|
158
|
+
while (this._toasts.length > max) {
|
|
159
|
+
const removed = this._toasts.shift();
|
|
160
|
+
this._clearTimer(removed.id);
|
|
161
|
+
}
|
|
162
|
+
this.render();
|
|
163
|
+
this._bindEvents();
|
|
164
|
+
if (toast.duration > 0) {
|
|
165
|
+
const timer = setTimeout(() => this._dismiss(toast.id), toast.duration);
|
|
166
|
+
this._timers.set(toast.id, timer);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
_dismiss(id) {
|
|
170
|
+
this._clearTimer(id);
|
|
171
|
+
const el = this.shadowRoot.querySelector(`[data-id="${id}"]`);
|
|
172
|
+
if (el) {
|
|
173
|
+
el.classList.add('out');
|
|
174
|
+
el.addEventListener('animationend', () => {
|
|
175
|
+
this._toasts = this._toasts.filter(t => t.id !== id);
|
|
176
|
+
this.render();
|
|
177
|
+
this._bindEvents();
|
|
178
|
+
}, { once: true });
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
this._toasts = this._toasts.filter(t => t.id !== id);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
_clearTimer(id) {
|
|
185
|
+
const t = this._timers.get(id);
|
|
186
|
+
if (t) {
|
|
187
|
+
clearTimeout(t);
|
|
188
|
+
this._timers.delete(id);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
onUnmount() {
|
|
192
|
+
this._timers.forEach(t => clearTimeout(t));
|
|
193
|
+
this._timers.clear();
|
|
194
|
+
}
|
|
195
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
196
|
+
if (oldValue !== newValue && this._mounted)
|
|
197
|
+
this.render();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
defineComponent('nc-snackbar', NcSnackbar);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nc-splash Component
|
|
3
|
+
*
|
|
4
|
+
* Standalone full-screen splash screen with dissolve effect.
|
|
5
|
+
* Shows branding then dissolves into particles.
|
|
6
|
+
* Use anywhere without needing to wrap content.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* </nc-splash>
|
|
10
|
+
*
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
import { Component } from '../core/component.js';
|
|
14
|
+
export declare class NcSplash extends Component {
|
|
15
|
+
static useShadowDOM: boolean;
|
|
16
|
+
private isComplete;
|
|
17
|
+
private canvas?;
|
|
18
|
+
private animationLoop?;
|
|
19
|
+
static get observedAttributes(): string[];
|
|
20
|
+
constructor();
|
|
21
|
+
template(): string;
|
|
22
|
+
onMount(): void;
|
|
23
|
+
private startDissolve;
|
|
24
|
+
disconnectedCallback(): void;
|
|
25
|
+
}
|