nativecorejs 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/README.md +22 -0
  2. package/dist/components/builtinRegistry.d.ts +2 -0
  3. package/dist/components/builtinRegistry.js +72 -0
  4. package/dist/components/index.d.ts +59 -0
  5. package/dist/components/index.js +59 -0
  6. package/dist/components/loading-spinner.d.ts +5 -0
  7. package/dist/components/loading-spinner.js +48 -0
  8. package/dist/components/nc-a.d.ts +45 -0
  9. package/dist/components/nc-a.js +290 -0
  10. package/dist/components/nc-accordion.d.ts +36 -0
  11. package/dist/components/nc-accordion.js +186 -0
  12. package/dist/components/nc-alert.d.ts +11 -0
  13. package/dist/components/nc-alert.js +127 -0
  14. package/dist/components/nc-animation.d.ts +117 -0
  15. package/dist/components/nc-animation.js +1053 -0
  16. package/dist/components/nc-autocomplete.d.ts +41 -0
  17. package/dist/components/nc-autocomplete.js +275 -0
  18. package/dist/components/nc-avatar-group.d.ts +7 -0
  19. package/dist/components/nc-avatar-group.js +85 -0
  20. package/dist/components/nc-avatar.d.ts +9 -0
  21. package/dist/components/nc-avatar.js +127 -0
  22. package/dist/components/nc-badge.d.ts +7 -0
  23. package/dist/components/nc-badge.js +63 -0
  24. package/dist/components/nc-bottom-nav.d.ts +53 -0
  25. package/dist/components/nc-bottom-nav.js +198 -0
  26. package/dist/components/nc-breadcrumb.d.ts +10 -0
  27. package/dist/components/nc-breadcrumb.js +71 -0
  28. package/dist/components/nc-button.d.ts +38 -0
  29. package/dist/components/nc-button.js +293 -0
  30. package/dist/components/nc-card.d.ts +11 -0
  31. package/dist/components/nc-card.js +74 -0
  32. package/dist/components/nc-checkbox.d.ts +16 -0
  33. package/dist/components/nc-checkbox.js +194 -0
  34. package/dist/components/nc-chip.d.ts +8 -0
  35. package/dist/components/nc-chip.js +89 -0
  36. package/dist/components/nc-code.d.ts +37 -0
  37. package/dist/components/nc-code.js +315 -0
  38. package/dist/components/nc-collapsible.d.ts +33 -0
  39. package/dist/components/nc-collapsible.js +148 -0
  40. package/dist/components/nc-color-picker.d.ts +33 -0
  41. package/dist/components/nc-color-picker.js +265 -0
  42. package/dist/components/nc-copy-button.d.ts +10 -0
  43. package/dist/components/nc-copy-button.js +94 -0
  44. package/dist/components/nc-date-picker.d.ts +41 -0
  45. package/dist/components/nc-date-picker.js +443 -0
  46. package/dist/components/nc-div.d.ts +53 -0
  47. package/dist/components/nc-div.js +270 -0
  48. package/dist/components/nc-divider.d.ts +7 -0
  49. package/dist/components/nc-divider.js +57 -0
  50. package/dist/components/nc-drawer.d.ts +40 -0
  51. package/dist/components/nc-drawer.js +217 -0
  52. package/dist/components/nc-dropdown.d.ts +41 -0
  53. package/dist/components/nc-dropdown.js +170 -0
  54. package/dist/components/nc-empty-state.d.ts +5 -0
  55. package/dist/components/nc-empty-state.js +76 -0
  56. package/dist/components/nc-file-upload.d.ts +40 -0
  57. package/dist/components/nc-file-upload.js +336 -0
  58. package/dist/components/nc-form.d.ts +70 -0
  59. package/dist/components/nc-form.js +273 -0
  60. package/dist/components/nc-image.d.ts +10 -0
  61. package/dist/components/nc-image.js +139 -0
  62. package/dist/components/nc-input.d.ts +25 -0
  63. package/dist/components/nc-input.js +302 -0
  64. package/dist/components/nc-kbd.d.ts +5 -0
  65. package/dist/components/nc-kbd.js +34 -0
  66. package/dist/components/nc-menu-item.d.ts +43 -0
  67. package/dist/components/nc-menu-item.js +182 -0
  68. package/dist/components/nc-menu.d.ts +76 -0
  69. package/dist/components/nc-menu.js +360 -0
  70. package/dist/components/nc-modal.d.ts +51 -0
  71. package/dist/components/nc-modal.js +231 -0
  72. package/dist/components/nc-nav-item.d.ts +35 -0
  73. package/dist/components/nc-nav-item.js +142 -0
  74. package/dist/components/nc-number-input.d.ts +22 -0
  75. package/dist/components/nc-number-input.js +270 -0
  76. package/dist/components/nc-otp-input.d.ts +41 -0
  77. package/dist/components/nc-otp-input.js +227 -0
  78. package/dist/components/nc-pagination.d.ts +28 -0
  79. package/dist/components/nc-pagination.js +171 -0
  80. package/dist/components/nc-popover.d.ts +58 -0
  81. package/dist/components/nc-popover.js +301 -0
  82. package/dist/components/nc-progress-circular.d.ts +7 -0
  83. package/dist/components/nc-progress-circular.js +67 -0
  84. package/dist/components/nc-progress.d.ts +7 -0
  85. package/dist/components/nc-progress.js +109 -0
  86. package/dist/components/nc-radio.d.ts +13 -0
  87. package/dist/components/nc-radio.js +169 -0
  88. package/dist/components/nc-rating.d.ts +19 -0
  89. package/dist/components/nc-rating.js +187 -0
  90. package/dist/components/nc-rich-text.d.ts +43 -0
  91. package/dist/components/nc-rich-text.js +310 -0
  92. package/dist/components/nc-scroll-top.d.ts +28 -0
  93. package/dist/components/nc-scroll-top.js +103 -0
  94. package/dist/components/nc-select.d.ts +51 -0
  95. package/dist/components/nc-select.js +425 -0
  96. package/dist/components/nc-skeleton.d.ts +7 -0
  97. package/dist/components/nc-skeleton.js +90 -0
  98. package/dist/components/nc-slider.d.ts +41 -0
  99. package/dist/components/nc-slider.js +268 -0
  100. package/dist/components/nc-snackbar.d.ts +51 -0
  101. package/dist/components/nc-snackbar.js +200 -0
  102. package/dist/components/nc-splash.d.ts +25 -0
  103. package/dist/components/nc-splash.js +296 -0
  104. package/dist/components/nc-stepper.d.ts +50 -0
  105. package/dist/components/nc-stepper.js +236 -0
  106. package/dist/components/nc-switch.d.ts +14 -0
  107. package/dist/components/nc-switch.js +194 -0
  108. package/dist/components/nc-tab-item.d.ts +39 -0
  109. package/dist/components/nc-tab-item.js +127 -0
  110. package/dist/components/nc-table.d.ts +44 -0
  111. package/dist/components/nc-table.js +265 -0
  112. package/dist/components/nc-tabs.d.ts +79 -0
  113. package/dist/components/nc-tabs.js +519 -0
  114. package/dist/components/nc-tag-input.d.ts +49 -0
  115. package/dist/components/nc-tag-input.js +268 -0
  116. package/dist/components/nc-textarea.d.ts +15 -0
  117. package/dist/components/nc-textarea.js +164 -0
  118. package/dist/components/nc-time-picker.d.ts +51 -0
  119. package/dist/components/nc-time-picker.js +452 -0
  120. package/dist/components/nc-timeline.d.ts +53 -0
  121. package/dist/components/nc-timeline.js +171 -0
  122. package/dist/components/nc-tooltip.d.ts +27 -0
  123. package/dist/components/nc-tooltip.js +135 -0
  124. package/dist/core/component.d.ts +33 -0
  125. package/dist/core/component.js +208 -0
  126. package/dist/core/gpu-animation.d.ts +141 -0
  127. package/dist/core/gpu-animation.js +474 -0
  128. package/dist/core/lazyComponents.d.ts +13 -0
  129. package/dist/core/lazyComponents.js +73 -0
  130. package/dist/core/router.d.ts +55 -0
  131. package/dist/core/router.js +424 -0
  132. package/dist/core/state.d.ts +18 -0
  133. package/dist/core/state.js +153 -0
  134. package/dist/index.d.ts +14 -0
  135. package/dist/index.js +11 -0
  136. package/dist/utils/cacheBuster.d.ts +9 -0
  137. package/dist/utils/cacheBuster.js +12 -0
  138. package/dist/utils/dom.d.ts +16 -0
  139. package/dist/utils/dom.js +70 -0
  140. package/dist/utils/events.d.ts +20 -0
  141. package/dist/utils/events.js +80 -0
  142. package/dist/utils/templates.d.ts +2 -0
  143. package/dist/utils/templates.js +2 -0
  144. package/package.json +53 -0
  145. package/src/styles/base.css +40 -0
@@ -0,0 +1,70 @@
1
+ /**
2
+ * NcForm + NcField Components
3
+ *
4
+ * nc-form:
5
+ * A form wrapper that collects values from all nc-* form controls within it,
6
+ * runs validation, and emits structured events.
7
+ *
8
+ * Attributes:
9
+ * - novalidate: boolean - skip HTML5 native validation
10
+ *
11
+ * Methods:
12
+ * - form.getValues(): Record<string, string> - collect all named form control values
13
+ * - form.validate(): boolean - trigger validation; returns true if all valid
14
+ * - form.reset() - reset all fields to initial value/clear errors
15
+ *
16
+ * Events:
17
+ * - submit: CustomEvent<{ values: Record<string, string> }> - on valid submit
18
+ * - invalid: CustomEvent<{ fields: string[] }> - on invalid submit (list of failing names)
19
+ *
20
+ * nc-field:
21
+ * A labelled wrapper for a single form control. Handles the label, hint, and
22
+ * error message display in a consistent layout. Pairs naturally with nc-input,
23
+ * nc-select, nc-textarea, nc-autocomplete, nc-date-picker, etc.
24
+ *
25
+ * Attributes:
26
+ * - label: string - field label
27
+ * - for: string - id of the slotted input (for focus on label click)
28
+ * - required: boolean - shows required marker
29
+ * - hint: string - sub-label hint text
30
+ * - error: string - error message (shown in red; overrides hint)
31
+ *
32
+ * Usage:
33
+ * <nc-form id="signup-form">
34
+ * <nc-field label="Email" required hint="We'll never share your email.">
35
+ * <nc-input name="email" type="email" placeholder="you@example.com"></nc-input>
36
+ * </nc-field>
37
+ * <nc-field label="Password" required>
38
+ * <nc-input name="password" type="password" show-password-toggle></nc-input>
39
+ * </nc-field>
40
+ * <nc-button type="submit">Sign up</nc-button>
41
+ * </nc-form>
42
+ *
43
+ * document.getElementById('signup-form').addEventListener('submit', e => {
44
+ * console.log(e.detail.values);
45
+ * });
46
+ */
47
+ import { Component } from '../core/component.js';
48
+ export declare class NcField extends Component {
49
+ static useShadowDOM: boolean;
50
+ static get observedAttributes(): string[];
51
+ template(): string;
52
+ setError(msg: string): void;
53
+ clearError(): void;
54
+ attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
55
+ }
56
+ export declare class NcForm extends Component {
57
+ static useShadowDOM: boolean;
58
+ private readonly _submitHandler;
59
+ private readonly _keydownHandler;
60
+ private readonly _clickHandler;
61
+ template(): string;
62
+ connectedCallback(): void;
63
+ disconnectedCallback(): void;
64
+ private _onSubmit;
65
+ private _handleSubmit;
66
+ getValues(): Record<string, string>;
67
+ validate(): boolean;
68
+ reset(): void;
69
+ attributeChangedCallback(_name: string, _oldValue: string, _newValue: string): void;
70
+ }
@@ -0,0 +1,273 @@
1
+ /**
2
+ * NcForm + NcField Components
3
+ *
4
+ * nc-form:
5
+ * A form wrapper that collects values from all nc-* form controls within it,
6
+ * runs validation, and emits structured events.
7
+ *
8
+ * Attributes:
9
+ * - novalidate: boolean - skip HTML5 native validation
10
+ *
11
+ * Methods:
12
+ * - form.getValues(): Record<string, string> - collect all named form control values
13
+ * - form.validate(): boolean - trigger validation; returns true if all valid
14
+ * - form.reset() - reset all fields to initial value/clear errors
15
+ *
16
+ * Events:
17
+ * - submit: CustomEvent<{ values: Record<string, string> }> - on valid submit
18
+ * - invalid: CustomEvent<{ fields: string[] }> - on invalid submit (list of failing names)
19
+ *
20
+ * nc-field:
21
+ * A labelled wrapper for a single form control. Handles the label, hint, and
22
+ * error message display in a consistent layout. Pairs naturally with nc-input,
23
+ * nc-select, nc-textarea, nc-autocomplete, nc-date-picker, etc.
24
+ *
25
+ * Attributes:
26
+ * - label: string - field label
27
+ * - for: string - id of the slotted input (for focus on label click)
28
+ * - required: boolean - shows required marker
29
+ * - hint: string - sub-label hint text
30
+ * - error: string - error message (shown in red; overrides hint)
31
+ *
32
+ * Usage:
33
+ * <nc-form id="signup-form">
34
+ * <nc-field label="Email" required hint="We'll never share your email.">
35
+ * <nc-input name="email" type="email" placeholder="you@example.com"></nc-input>
36
+ * </nc-field>
37
+ * <nc-field label="Password" required>
38
+ * <nc-input name="password" type="password" show-password-toggle></nc-input>
39
+ * </nc-field>
40
+ * <nc-button type="submit">Sign up</nc-button>
41
+ * </nc-form>
42
+ *
43
+ * document.getElementById('signup-form').addEventListener('submit', e => {
44
+ * console.log(e.detail.values);
45
+ * });
46
+ */
47
+ import { Component, defineComponent } from '../core/component.js';
48
+ // ── NcField ──────────────────────────────────────────────────────────────────
49
+ export class NcField extends Component {
50
+ static useShadowDOM = true;
51
+ static get observedAttributes() {
52
+ return ['label', 'for', 'required', 'hint', 'error'];
53
+ }
54
+ template() {
55
+ const label = this.getAttribute('label') || '';
56
+ const forAttr = this.getAttribute('for') || '';
57
+ const required = this.hasAttribute('required');
58
+ const hint = this.getAttribute('hint') || '';
59
+ const error = this.getAttribute('error') || '';
60
+ return `
61
+ <style>
62
+ :host { display: block; font-family: var(--nc-font-family); }
63
+
64
+ .field { display: flex; flex-direction: column; gap: 4px; }
65
+
66
+ label {
67
+ font-size: var(--nc-font-size-sm);
68
+ font-weight: var(--nc-font-weight-medium);
69
+ color: var(--nc-text);
70
+ cursor: ${forAttr ? 'pointer' : 'default'};
71
+ }
72
+
73
+ .required {
74
+ color: var(--nc-danger, #ef4444);
75
+ margin-left: 2px;
76
+ }
77
+
78
+ .subtext {
79
+ font-size: var(--nc-font-size-xs);
80
+ line-height: 1.4;
81
+ }
82
+ .subtext--hint { color: var(--nc-text-muted); }
83
+ .subtext--error { color: var(--nc-danger, #ef4444); }
84
+ </style>
85
+ <div class="field">
86
+ ${label ? `
87
+ <label ${forAttr ? `for="${forAttr}"` : ''}>
88
+ ${label}${required ? `<span class="required" aria-hidden="true">*</span>` : ''}
89
+ </label>` : ''}
90
+ <slot></slot>
91
+ ${error
92
+ ? `<span class="subtext subtext--error" role="alert">${error}</span>`
93
+ : hint ? `<span class="subtext subtext--hint">${hint}</span>` : ''}
94
+ </div>
95
+ `;
96
+ }
97
+ setError(msg) {
98
+ if (msg)
99
+ this.setAttribute('error', msg);
100
+ else
101
+ this.removeAttribute('error');
102
+ }
103
+ clearError() { this.removeAttribute('error'); }
104
+ attributeChangedCallback(name, oldValue, newValue) {
105
+ if (oldValue !== newValue && this._mounted)
106
+ this.render();
107
+ }
108
+ }
109
+ defineComponent('nc-field', NcField);
110
+ // ── NcForm ───────────────────────────────────────────────────────────────────
111
+ const FORM_CONTROLS = [
112
+ 'nc-input', 'nc-textarea', 'nc-select', 'nc-checkbox', 'nc-radio',
113
+ 'nc-switch', 'nc-slider', 'nc-rating', 'nc-number-input',
114
+ 'nc-autocomplete', 'nc-date-picker', 'nc-time-picker', 'nc-color-picker',
115
+ 'input', 'textarea', 'select',
116
+ ];
117
+ export class NcForm extends Component {
118
+ static useShadowDOM = true;
119
+ _submitHandler = this._onSubmit.bind(this);
120
+ _keydownHandler = (e) => {
121
+ const ke = e;
122
+ const target = ke.target;
123
+ if (ke.key === 'Enter' && target.tagName !== 'TEXTAREA') {
124
+ this._handleSubmit();
125
+ }
126
+ };
127
+ _clickHandler = (e) => {
128
+ const btn = e.target.closest('[type="submit"], nc-button[type="submit"]');
129
+ if (btn && this.contains(btn)) {
130
+ e.preventDefault();
131
+ this._handleSubmit();
132
+ }
133
+ };
134
+ template() {
135
+ return `
136
+ <style>
137
+ :host {
138
+ display: block;
139
+ width: 100%;
140
+ }
141
+ </style>
142
+ <slot></slot>
143
+ `;
144
+ }
145
+ connectedCallback() {
146
+ super.connectedCallback?.();
147
+ this.addEventListener('submit', this._submitHandler);
148
+ this.addEventListener('keydown', this._keydownHandler);
149
+ this.addEventListener('click', this._clickHandler);
150
+ }
151
+ disconnectedCallback() {
152
+ this.removeEventListener('submit', this._submitHandler);
153
+ this.removeEventListener('keydown', this._keydownHandler);
154
+ this.removeEventListener('click', this._clickHandler);
155
+ super.disconnectedCallback?.();
156
+ }
157
+ _onSubmit(e) {
158
+ const customEvent = e;
159
+ if (customEvent.detail?.values) {
160
+ return;
161
+ }
162
+ e.preventDefault();
163
+ this._handleSubmit();
164
+ }
165
+ _handleSubmit() {
166
+ if (!this.hasAttribute('novalidate')) {
167
+ const valid = this.validate();
168
+ if (!valid)
169
+ return;
170
+ }
171
+ const values = this.getValues();
172
+ this.dispatchEvent(new CustomEvent('submit', {
173
+ bubbles: true, composed: true,
174
+ detail: { values }
175
+ }));
176
+ }
177
+ getValues() {
178
+ const result = {};
179
+ FORM_CONTROLS.forEach(tag => {
180
+ this.querySelectorAll(tag).forEach(el => {
181
+ const name = el.getAttribute('name');
182
+ if (!name)
183
+ return;
184
+ // nc-checkbox/nc-switch: use checked state
185
+ if (tag === 'nc-checkbox' || tag === 'nc-switch') {
186
+ result[name] = el.hasAttribute('checked') ? 'true' : 'false';
187
+ }
188
+ else {
189
+ const controlWithValue = el;
190
+ result[name] = controlWithValue.value ?? el.getAttribute('value') ?? el.value ?? '';
191
+ }
192
+ });
193
+ });
194
+ return result;
195
+ }
196
+ validate() {
197
+ let valid = true;
198
+ const invalidFields = [];
199
+ // Check nc-field wrappers first
200
+ this.querySelectorAll('nc-field').forEach(field => {
201
+ // Find the first named control inside this field
202
+ const ctrl = FORM_CONTROLS.map(tag => field.querySelector(tag)).find(Boolean);
203
+ if (!ctrl)
204
+ return;
205
+ const name = ctrl.getAttribute('name') || '';
206
+ const value = ctrl.value ?? ctrl.getAttribute('value') ?? ctrl.value ?? '';
207
+ const isRequired = field.hasAttribute('required') || ctrl.hasAttribute('required');
208
+ if (isRequired && !String(value).trim()) {
209
+ valid = false;
210
+ field.setError('This field is required.');
211
+ invalidFields.push(name);
212
+ ctrl.clearValidationError?.();
213
+ return;
214
+ }
215
+ if (typeof ctrl.checkValidity === 'function' && !ctrl.checkValidity()) {
216
+ valid = false;
217
+ field.setError(typeof ctrl.getValidationMessage === 'function'
218
+ ? ctrl.getValidationMessage()
219
+ : 'Enter a valid value.');
220
+ invalidFields.push(name);
221
+ ctrl.clearValidationError?.();
222
+ return;
223
+ }
224
+ field.clearError();
225
+ ctrl.clearValidationError?.();
226
+ });
227
+ // Validate standalone controls not wrapped in nc-field.
228
+ FORM_CONTROLS.forEach(tag => {
229
+ this.querySelectorAll(tag).forEach(ctrl => {
230
+ if (ctrl.closest('nc-field'))
231
+ return;
232
+ const element = ctrl;
233
+ if (typeof element.validate === 'function') {
234
+ const isValid = element.validate();
235
+ if (!isValid) {
236
+ valid = false;
237
+ const name = ctrl.getAttribute('name');
238
+ if (name)
239
+ invalidFields.push(name);
240
+ }
241
+ return;
242
+ }
243
+ if (typeof element.checkValidity === 'function' && !element.checkValidity()) {
244
+ valid = false;
245
+ const name = ctrl.getAttribute('name');
246
+ if (name)
247
+ invalidFields.push(name);
248
+ return;
249
+ }
250
+ element.clearValidationError?.();
251
+ });
252
+ });
253
+ if (!valid) {
254
+ this.dispatchEvent(new CustomEvent('invalid', {
255
+ bubbles: true, composed: true,
256
+ detail: { fields: Array.from(new Set(invalidFields)) }
257
+ }));
258
+ }
259
+ return valid;
260
+ }
261
+ reset() {
262
+ FORM_CONTROLS.forEach(tag => {
263
+ this.querySelectorAll(tag).forEach(el => {
264
+ el.setAttribute('value', '');
265
+ if (tag === 'nc-checkbox' || tag === 'nc-switch')
266
+ el.removeAttribute('checked');
267
+ });
268
+ });
269
+ this.querySelectorAll('nc-field').forEach(f => f.clearError());
270
+ }
271
+ attributeChangedCallback(_name, _oldValue, _newValue) { }
272
+ }
273
+ defineComponent('nc-form', NcForm);
@@ -0,0 +1,10 @@
1
+ import { Component } from '../core/component.js';
2
+ export declare class NcImage extends Component {
3
+ static useShadowDOM: boolean;
4
+ private loaded;
5
+ private errored;
6
+ static get observedAttributes(): string[];
7
+ template(): string;
8
+ onMount(): void;
9
+ attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
10
+ }
@@ -0,0 +1,139 @@
1
+ import { Component, defineComponent } from '../core/component.js';
2
+ const RADIUS = {
3
+ none: '0',
4
+ sm: 'var(--nc-radius-sm, 0.375rem)',
5
+ md: 'var(--nc-radius-md, 0.5rem)',
6
+ lg: 'var(--nc-radius-lg, 1rem)',
7
+ full: '9999px'
8
+ };
9
+ export class NcImage extends Component {
10
+ static useShadowDOM = true;
11
+ loaded = false;
12
+ errored = false;
13
+ static get observedAttributes() {
14
+ return ['src', 'alt', 'width', 'height', 'fit', 'radius', 'aspect', 'fallback'];
15
+ }
16
+ template() {
17
+ const src = this.getAttribute('src') ?? '';
18
+ const alt = this.getAttribute('alt') ?? '';
19
+ const width = this.getAttribute('width') ?? '';
20
+ const height = this.getAttribute('height') ?? '';
21
+ const fit = this.getAttribute('fit') ?? 'cover';
22
+ const position = this.getAttribute('position') ?? 'center';
23
+ const radius = this.getAttribute('radius') ?? 'none';
24
+ const loading = this.getAttribute('loading') ?? 'lazy';
25
+ const placeholder = this.getAttribute('placeholder') ?? 'skeleton';
26
+ const aspect = this.getAttribute('aspect') ?? '';
27
+ const caption = this.getAttribute('caption') ?? '';
28
+ const radiusValue = RADIUS[radius] ?? radius;
29
+ const aspectStyle = aspect ? `aspect-ratio: ${aspect};` : '';
30
+ const widthStyle = width ? `width:${/^\d+$/.test(width) ? width + 'px' : width};` : '';
31
+ const heightStyle = height ? `height:${/^\d+$/.test(height) ? height + 'px' : height};` : '';
32
+ const showSkeleton = !this.loaded && !this.errored && placeholder === 'skeleton';
33
+ return `
34
+ <style>
35
+ :host { display: inline-block; }
36
+ figure {
37
+ margin: 0;
38
+ padding: 0;
39
+ display: block;
40
+ ${widthStyle} ${heightStyle}
41
+ border-radius: ${radiusValue};
42
+ overflow: hidden;
43
+ position: relative;
44
+ ${aspectStyle}
45
+ }
46
+ .skeleton {
47
+ position: absolute;
48
+ inset: 0;
49
+ background: linear-gradient(90deg, var(--nc-bg-secondary, #f8fafc) 25%, var(--nc-bg-tertiary, #e2e8f0) 50%, var(--nc-bg-secondary, #f8fafc) 75%);
50
+ background-size: 200% 100%;
51
+ animation: nc-img-shimmer 1.4s infinite linear;
52
+ border-radius: inherit;
53
+ display: ${showSkeleton ? 'block' : 'none'};
54
+ }
55
+ @keyframes nc-img-shimmer {
56
+ 0% { background-position: -200% 0; }
57
+ 100% { background-position: 200% 0; }
58
+ }
59
+ img {
60
+ display: block;
61
+ width: 100%;
62
+ height: 100%;
63
+ object-fit: ${fit};
64
+ object-position: ${position};
65
+ border-radius: inherit;
66
+ opacity: ${this.loaded ? 1 : 0};
67
+ transition: opacity var(--nc-transition-base, 220ms ease);
68
+ }
69
+ .error-plate {
70
+ display: ${this.errored ? 'flex' : 'none'};
71
+ align-items: center;
72
+ justify-content: center;
73
+ position: absolute;
74
+ inset: 0;
75
+ background: var(--nc-bg-secondary, #f8fafc);
76
+ color: var(--nc-text-muted, #6b7280);
77
+ font-size: var(--nc-font-size-xs, 0.75rem);
78
+ font-family: var(--nc-font-family);
79
+ flex-direction: column;
80
+ gap: 4px;
81
+ }
82
+ figcaption {
83
+ font-family: var(--nc-font-family);
84
+ font-size: var(--nc-font-size-xs, 0.75rem);
85
+ color: var(--nc-text-muted, #6b7280);
86
+ text-align: center;
87
+ padding-top: 4px;
88
+ line-height: 1.4;
89
+ }
90
+ </style>
91
+ <figure>
92
+ <div class="skeleton"></div>
93
+ <img id="img" src="${src}" alt="${alt}" loading="${loading}" decoding="async" ${width ? `width="${width}"` : ''} ${height ? `height="${height}"` : ''} />
94
+ <div class="error-plate" aria-hidden="true">
95
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
96
+ <span>Image not found</span>
97
+ </div>
98
+ </figure>
99
+ ${caption ? `<figcaption>${caption}</figcaption>` : ''}
100
+ `;
101
+ }
102
+ onMount() {
103
+ const image = this.$('#img');
104
+ if (!image)
105
+ return;
106
+ if (image.complete && image.naturalWidth > 0) {
107
+ this.loaded = true;
108
+ this.render();
109
+ return;
110
+ }
111
+ image.addEventListener('load', () => {
112
+ this.loaded = true;
113
+ this.errored = false;
114
+ this.render();
115
+ this.dispatchEvent(new CustomEvent('load', { bubbles: true, composed: true }));
116
+ }, { once: true });
117
+ image.addEventListener('error', () => {
118
+ const fallback = this.getAttribute('fallback');
119
+ if (fallback && image.src !== fallback) {
120
+ image.src = fallback;
121
+ return;
122
+ }
123
+ this.errored = true;
124
+ this.loaded = false;
125
+ this.render();
126
+ this.dispatchEvent(new CustomEvent('error', { bubbles: true, composed: true }));
127
+ }, { once: true });
128
+ }
129
+ attributeChangedCallback(name, oldValue, newValue) {
130
+ if (oldValue === newValue || !this._mounted)
131
+ return;
132
+ if (name === 'src') {
133
+ this.loaded = false;
134
+ this.errored = false;
135
+ }
136
+ this.render();
137
+ }
138
+ }
139
+ defineComponent('nc-image', NcImage);
@@ -0,0 +1,25 @@
1
+ import { Component } from '../core/component.js';
2
+ export declare class NcInput extends Component {
3
+ static useShadowDOM: boolean;
4
+ static get observedAttributes(): string[];
5
+ private currentValue;
6
+ private showPassword;
7
+ private validationError;
8
+ private readonly handleInputEvent;
9
+ private readonly handleChangeEvent;
10
+ private readonly handleClickEvent;
11
+ get value(): string;
12
+ set value(nextValue: string);
13
+ template(): string;
14
+ onMount(): void;
15
+ onUnmount(): void;
16
+ private syncClearButton;
17
+ private getInput;
18
+ private buildValidationMessage;
19
+ getValidationMessage(): string;
20
+ checkValidity(): boolean;
21
+ validate(): boolean;
22
+ reportValidity(): boolean;
23
+ clearValidationError(): void;
24
+ attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
25
+ }