tecnualng 21.0.25 → 21.0.27

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.
@@ -232,21 +232,146 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
232
232
  args: ['change']
233
233
  }] } });
234
234
 
235
+ class TngTextareaDirective {
236
+ el = inject((ElementRef));
237
+ ngControl = inject(NgControl, { optional: true, self: true });
238
+ // Inputs
239
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
240
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
241
+ maxLength = input(undefined, ...(ngDevMode ? [{ debugName: "maxLength" }] : []));
242
+ // State signals
243
+ focused = signal(false, ...(ngDevMode ? [{ debugName: "focused" }] : []));
244
+ _value = signal('', ...(ngDevMode ? [{ debugName: "_value" }] : []));
245
+ // Computed
246
+ hasValue = computed(() => {
247
+ const val = this._value();
248
+ return val !== null && val !== undefined && val !== '';
249
+ }, ...(ngDevMode ? [{ debugName: "hasValue" }] : []));
250
+ valueLength = computed(() => this._value().length, ...(ngDevMode ? [{ debugName: "valueLength" }] : []));
251
+ computedPlaceholder = computed(() => {
252
+ return this.focused() ? this.placeholder() : '';
253
+ }, ...(ngDevMode ? [{ debugName: "computedPlaceholder" }] : []));
254
+ constructor() {
255
+ effect(() => {
256
+ this.updateValue();
257
+ }, { allowSignalWrites: true });
258
+ }
259
+ onFocus() {
260
+ this.focused.set(true);
261
+ }
262
+ onBlur() {
263
+ this.focused.set(false);
264
+ this.updateValue();
265
+ }
266
+ onInput() {
267
+ this.updateValue();
268
+ }
269
+ ngAfterViewInit() {
270
+ this.updateValue();
271
+ }
272
+ updateValue() {
273
+ this._value.set(this.el.nativeElement.value);
274
+ }
275
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: TngTextareaDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
276
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.0", type: TngTextareaDirective, isStandalone: true, selector: "textarea[tngTextarea]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "focus": "onFocus()", "blur": "onBlur()", "input": "onInput()" }, properties: { "class.tng-textarea-field": "true", "disabled": "disabled()", "attr.maxlength": "maxLength() || null", "attr.placeholder": "computedPlaceholder()" } }, exportAs: ["tngTextarea"], ngImport: i0 });
277
+ }
278
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: TngTextareaDirective, decorators: [{
279
+ type: Directive,
280
+ args: [{
281
+ selector: 'textarea[tngTextarea]',
282
+ standalone: true,
283
+ host: {
284
+ '[class.tng-textarea-field]': 'true',
285
+ '[disabled]': 'disabled()',
286
+ '[attr.maxlength]': 'maxLength() || null',
287
+ '[attr.placeholder]': 'computedPlaceholder()'
288
+ },
289
+ exportAs: 'tngTextarea'
290
+ }]
291
+ }], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], onFocus: [{
292
+ type: HostListener,
293
+ args: ['focus']
294
+ }], onBlur: [{
295
+ type: HostListener,
296
+ args: ['blur']
297
+ }], onInput: [{
298
+ type: HostListener,
299
+ args: ['input']
300
+ }] } });
301
+
235
302
  class TngFormFieldComponent {
236
303
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
237
- // Query for the input directive inside this component
304
+ // Query for the input or textarea directive inside this component
238
305
  inputDirective = contentChild(TngInputDirective, ...(ngDevMode ? [{ debugName: "inputDirective" }] : []));
239
- // Computed states based on the input directive
240
- isFocused = computed(() => this.inputDirective()?.focused() ?? false, ...(ngDevMode ? [{ debugName: "isFocused" }] : []));
241
- hasValue = computed(() => this.inputDirective()?.hasValue() ?? false, ...(ngDevMode ? [{ debugName: "hasValue" }] : []));
242
- isDisabled = computed(() => this.inputDirective()?.disabled() ?? false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
306
+ textareaDirective = contentChild(TngTextareaDirective, ...(ngDevMode ? [{ debugName: "textareaDirective" }] : []));
307
+ // Computed states based on the input/textarea directive
308
+ activeDirective = computed(() => this.inputDirective() || this.textareaDirective(), ...(ngDevMode ? [{ debugName: "activeDirective" }] : []));
309
+ isFocused = computed(() => this.activeDirective()?.focused() ?? false, ...(ngDevMode ? [{ debugName: "isFocused" }] : []));
310
+ hasValue = computed(() => this.activeDirective()?.hasValue() ?? false, ...(ngDevMode ? [{ debugName: "hasValue" }] : []));
311
+ isDisabled = computed(() => this.activeDirective()?.disabled() ?? false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
312
+ // Character count logic
313
+ maxLength = computed(() => this.textareaDirective()?.maxLength(), ...(ngDevMode ? [{ debugName: "maxLength" }] : []));
314
+ currentLength = computed(() => this.textareaDirective()?.valueLength() ?? 0, ...(ngDevMode ? [{ debugName: "currentLength" }] : []));
315
+ showCounter = computed(() => this.maxLength() !== undefined, ...(ngDevMode ? [{ debugName: "showCounter" }] : []));
316
+ // Style helper
317
+ hasTextarea = computed(() => !!this.textareaDirective(), ...(ngDevMode ? [{ debugName: "hasTextarea" }] : []));
243
318
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: TngFormFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
244
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.0", type: TngFormFieldComponent, isStandalone: true, selector: "tng-form-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "inputDirective", first: true, predicate: TngInputDirective, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"tng-input-container\" \n [class.focused]=\"isFocused()\" \n [class.has-value]=\"hasValue()\" \n [class.disabled]=\"isDisabled()\">\n <fieldset class=\"tng-fieldset\" aria-hidden=\"true\">\n <legend class=\"tng-legend\"><span>{{ label() }}</span></legend>\n </fieldset>\n <label class=\"tng-label\">{{ label() }}</label>\n <ng-content></ng-content>\n</div>\n", styles: [":host{display:block;margin-bottom:1.5rem;font-family:var(--tng-font-family, \"Inter\", sans-serif)}.tng-input-container{position:relative;border-radius:var(--tng-border-radius, 4px);padding:0 16px;min-height:48px;display:flex;align-items:center;cursor:text;border:none}.tng-input-container:hover .tng-fieldset{border-color:var(--tng-text-secondary, #757575)}.tng-input-container.focused .tng-fieldset{border-color:var(--tng-primary, #6200ee);border-width:2px}.tng-input-container.focused .tng-label{color:var(--tng-primary, #6200ee);transform:translateY(-24px) scale(.75)}.tng-input-container.focused .tng-legend{max-width:100%}.tng-input-container.has-value .tng-label{transform:translateY(-24px) scale(.75)}.tng-input-container.has-value .tng-legend{max-width:100%}.tng-input-container.disabled{opacity:.6;pointer-events:none}.tng-input-container.disabled .tng-fieldset{border-color:var(--tng-border, #e0e0e0);background-color:var(--tng-background, #fafafa)}.tng-fieldset{position:absolute;inset:0 0 5px;margin:0;padding:0 12px;border:1px solid var(--tng-border, #999);border-radius:var(--tng-border-radius, 4px);pointer-events:none;transition:border-color .2s ease,border-width .1s ease;z-index:0}.tng-legend{width:auto;max-width:0;height:11px;font-size:.75em;visibility:hidden;transition:max-width .1s cubic-bezier(.4,0,.2,1);white-space:nowrap;padding-inline:0px}.tng-legend span{padding:0 2px;visibility:visible;opacity:0}.tng-label{position:absolute;top:50%;left:16px;transform:translateY(-50%);color:var(--tng-text-secondary, #666);font-size:16px;font-weight:400;pointer-events:none;transform-origin:left top;transition:transform .2s cubic-bezier(.4,0,.2,1),color .2s ease;z-index:1}input.tng-input-field,textarea.tng-input-field{display:block;width:100%;border:none;background:none;padding:0;margin:0;font-size:16px;color:var(--tng-text, #333);outline:none;height:100%;min-height:48px;line-height:normal;z-index:1;font-family:inherit}input.tng-input-field::placeholder,textarea.tng-input-field::placeholder{color:transparent;transition:color .2s ease}input.tng-input-field:focus::placeholder,textarea.tng-input-field:focus::placeholder{color:#aaa}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], encapsulation: i0.ViewEncapsulation.None });
319
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.0", type: TngFormFieldComponent, isStandalone: true, selector: "tng-form-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "inputDirective", first: true, predicate: TngInputDirective, descendants: true, isSignal: true }, { propertyName: "textareaDirective", first: true, predicate: TngTextareaDirective, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"tng-input-container\" \n [class.focused]=\"isFocused()\" \n [class.has-value]=\"hasValue()\" \n [class.disabled]=\"isDisabled()\"\n [class.tng-form-field-type-textarea]=\"hasTextarea()\">\n <fieldset class=\"tng-fieldset\" aria-hidden=\"true\">\n <legend class=\"tng-legend\"><span>{{ label() }}</span></legend>\n </fieldset>\n <label class=\"tng-label\">{{ label() }}</label>\n <ng-content></ng-content>\n</div>\n<div class=\"tng-form-field-subscript-wrapper\" *ngIf=\"showCounter()\">\n <div class=\"tng-form-field-spacer\"></div>\n <div class=\"tng-form-field-hint\">\n {{ currentLength() }} / {{ maxLength() }}\n </div>\n</div>\n", styles: [":host{display:block;margin-bottom:1.5rem;font-family:var(--tng-font-family, \"Inter\", sans-serif)}.tng-input-container{position:relative;border-radius:var(--tng-border-radius, 4px);padding:0;min-height:48px;display:flex;align-items:center;cursor:text;border:none}.tng-input-container:hover .tng-fieldset{border-color:var(--tng-text-secondary, #757575)}.tng-input-container.focused .tng-fieldset{border-color:var(--tng-primary, #6200ee);border-width:2px}.tng-input-container.focused .tng-label{color:var(--tng-primary, #6200ee);transform:translateY(-24px) scale(.75)}.tng-input-container.focused .tng-legend{max-width:100%}.tng-input-container.has-value .tng-label{transform:translateY(-24px) scale(.75)}.tng-input-container.has-value .tng-legend{max-width:100%}.tng-input-container.disabled{opacity:.6;pointer-events:none}.tng-input-container.disabled .tng-fieldset{border-color:var(--tng-border, #e0e0e0);background-color:var(--tng-background, #fafafa)}.tng-input-container.tng-form-field-type-textarea{align-items:flex-start;height:auto}.tng-input-container.tng-form-field-type-textarea .tng-label{top:24px}.tng-input-container.tng-form-field-type-textarea.focused .tng-label,.tng-input-container.tng-form-field-type-textarea.has-value .tng-label{transform:translateY(-24px) scale(.75)}.tng-fieldset{position:absolute;inset:0;margin:0;padding:0 12px;border:1px solid var(--tng-border, #999);border-radius:var(--tng-border-radius, 4px);pointer-events:none;transition:border-color .2s ease,border-width .1s ease;z-index:0}.tng-legend{width:auto;max-width:0;height:11px;font-size:.75em;visibility:hidden;transition:max-width .1s cubic-bezier(.4,0,.2,1);white-space:nowrap;padding-inline:0px}.tng-legend span{padding:0 2px;visibility:visible;opacity:0}.tng-label{position:absolute;top:50%;left:16px;transform:translateY(-50%);color:var(--tng-text-secondary, #666);font-size:16px;font-weight:400;pointer-events:none;transform-origin:left top;transition:transform .2s cubic-bezier(.4,0,.2,1),color .2s ease;z-index:1;width:calc(100% - 32px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}input.tng-input-field,textarea.tng-input-field,textarea.tng-textarea-field{display:block;width:100%;border:none;background:none;padding:0 16px;margin:0;font-size:16px;color:var(--tng-text, #333);outline:none;min-height:48px;line-height:normal;z-index:1;font-family:inherit;box-sizing:border-box}input.tng-input-field::placeholder,textarea.tng-input-field::placeholder,textarea.tng-textarea-field::placeholder{color:transparent;transition:color .2s ease}input.tng-input-field:focus::placeholder,textarea.tng-input-field:focus::placeholder,textarea.tng-textarea-field:focus::placeholder{color:#aaa}textarea.tng-textarea-field{padding-top:12px;padding-bottom:12px;resize:vertical;height:auto;line-height:1.5}.tng-form-field-subscript-wrapper{margin-top:4px;font-size:.75rem;display:flex;justify-content:space-between;padding:0 16px}.tng-form-field-spacer{flex:1 1 auto}.tng-form-field-hint{color:var(--tng-text-secondary, #666);text-align:right}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], encapsulation: i0.ViewEncapsulation.None });
245
320
  }
246
321
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: TngFormFieldComponent, decorators: [{
247
322
  type: Component,
248
- args: [{ selector: 'tng-form-field', standalone: true, imports: [CommonModule], encapsulation: ViewEncapsulation.None, template: "<div class=\"tng-input-container\" \n [class.focused]=\"isFocused()\" \n [class.has-value]=\"hasValue()\" \n [class.disabled]=\"isDisabled()\">\n <fieldset class=\"tng-fieldset\" aria-hidden=\"true\">\n <legend class=\"tng-legend\"><span>{{ label() }}</span></legend>\n </fieldset>\n <label class=\"tng-label\">{{ label() }}</label>\n <ng-content></ng-content>\n</div>\n", styles: [":host{display:block;margin-bottom:1.5rem;font-family:var(--tng-font-family, \"Inter\", sans-serif)}.tng-input-container{position:relative;border-radius:var(--tng-border-radius, 4px);padding:0 16px;min-height:48px;display:flex;align-items:center;cursor:text;border:none}.tng-input-container:hover .tng-fieldset{border-color:var(--tng-text-secondary, #757575)}.tng-input-container.focused .tng-fieldset{border-color:var(--tng-primary, #6200ee);border-width:2px}.tng-input-container.focused .tng-label{color:var(--tng-primary, #6200ee);transform:translateY(-24px) scale(.75)}.tng-input-container.focused .tng-legend{max-width:100%}.tng-input-container.has-value .tng-label{transform:translateY(-24px) scale(.75)}.tng-input-container.has-value .tng-legend{max-width:100%}.tng-input-container.disabled{opacity:.6;pointer-events:none}.tng-input-container.disabled .tng-fieldset{border-color:var(--tng-border, #e0e0e0);background-color:var(--tng-background, #fafafa)}.tng-fieldset{position:absolute;inset:0 0 5px;margin:0;padding:0 12px;border:1px solid var(--tng-border, #999);border-radius:var(--tng-border-radius, 4px);pointer-events:none;transition:border-color .2s ease,border-width .1s ease;z-index:0}.tng-legend{width:auto;max-width:0;height:11px;font-size:.75em;visibility:hidden;transition:max-width .1s cubic-bezier(.4,0,.2,1);white-space:nowrap;padding-inline:0px}.tng-legend span{padding:0 2px;visibility:visible;opacity:0}.tng-label{position:absolute;top:50%;left:16px;transform:translateY(-50%);color:var(--tng-text-secondary, #666);font-size:16px;font-weight:400;pointer-events:none;transform-origin:left top;transition:transform .2s cubic-bezier(.4,0,.2,1),color .2s ease;z-index:1}input.tng-input-field,textarea.tng-input-field{display:block;width:100%;border:none;background:none;padding:0;margin:0;font-size:16px;color:var(--tng-text, #333);outline:none;height:100%;min-height:48px;line-height:normal;z-index:1;font-family:inherit}input.tng-input-field::placeholder,textarea.tng-input-field::placeholder{color:transparent;transition:color .2s ease}input.tng-input-field:focus::placeholder,textarea.tng-input-field:focus::placeholder{color:#aaa}\n"] }]
249
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], inputDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TngInputDirective), { isSignal: true }] }] } });
323
+ args: [{ selector: 'tng-form-field', standalone: true, imports: [CommonModule], encapsulation: ViewEncapsulation.None, template: "<div class=\"tng-input-container\" \n [class.focused]=\"isFocused()\" \n [class.has-value]=\"hasValue()\" \n [class.disabled]=\"isDisabled()\"\n [class.tng-form-field-type-textarea]=\"hasTextarea()\">\n <fieldset class=\"tng-fieldset\" aria-hidden=\"true\">\n <legend class=\"tng-legend\"><span>{{ label() }}</span></legend>\n </fieldset>\n <label class=\"tng-label\">{{ label() }}</label>\n <ng-content></ng-content>\n</div>\n<div class=\"tng-form-field-subscript-wrapper\" *ngIf=\"showCounter()\">\n <div class=\"tng-form-field-spacer\"></div>\n <div class=\"tng-form-field-hint\">\n {{ currentLength() }} / {{ maxLength() }}\n </div>\n</div>\n", styles: [":host{display:block;margin-bottom:1.5rem;font-family:var(--tng-font-family, \"Inter\", sans-serif)}.tng-input-container{position:relative;border-radius:var(--tng-border-radius, 4px);padding:0;min-height:48px;display:flex;align-items:center;cursor:text;border:none}.tng-input-container:hover .tng-fieldset{border-color:var(--tng-text-secondary, #757575)}.tng-input-container.focused .tng-fieldset{border-color:var(--tng-primary, #6200ee);border-width:2px}.tng-input-container.focused .tng-label{color:var(--tng-primary, #6200ee);transform:translateY(-24px) scale(.75)}.tng-input-container.focused .tng-legend{max-width:100%}.tng-input-container.has-value .tng-label{transform:translateY(-24px) scale(.75)}.tng-input-container.has-value .tng-legend{max-width:100%}.tng-input-container.disabled{opacity:.6;pointer-events:none}.tng-input-container.disabled .tng-fieldset{border-color:var(--tng-border, #e0e0e0);background-color:var(--tng-background, #fafafa)}.tng-input-container.tng-form-field-type-textarea{align-items:flex-start;height:auto}.tng-input-container.tng-form-field-type-textarea .tng-label{top:24px}.tng-input-container.tng-form-field-type-textarea.focused .tng-label,.tng-input-container.tng-form-field-type-textarea.has-value .tng-label{transform:translateY(-24px) scale(.75)}.tng-fieldset{position:absolute;inset:0;margin:0;padding:0 12px;border:1px solid var(--tng-border, #999);border-radius:var(--tng-border-radius, 4px);pointer-events:none;transition:border-color .2s ease,border-width .1s ease;z-index:0}.tng-legend{width:auto;max-width:0;height:11px;font-size:.75em;visibility:hidden;transition:max-width .1s cubic-bezier(.4,0,.2,1);white-space:nowrap;padding-inline:0px}.tng-legend span{padding:0 2px;visibility:visible;opacity:0}.tng-label{position:absolute;top:50%;left:16px;transform:translateY(-50%);color:var(--tng-text-secondary, #666);font-size:16px;font-weight:400;pointer-events:none;transform-origin:left top;transition:transform .2s cubic-bezier(.4,0,.2,1),color .2s ease;z-index:1;width:calc(100% - 32px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}input.tng-input-field,textarea.tng-input-field,textarea.tng-textarea-field{display:block;width:100%;border:none;background:none;padding:0 16px;margin:0;font-size:16px;color:var(--tng-text, #333);outline:none;min-height:48px;line-height:normal;z-index:1;font-family:inherit;box-sizing:border-box}input.tng-input-field::placeholder,textarea.tng-input-field::placeholder,textarea.tng-textarea-field::placeholder{color:transparent;transition:color .2s ease}input.tng-input-field:focus::placeholder,textarea.tng-input-field:focus::placeholder,textarea.tng-textarea-field:focus::placeholder{color:#aaa}textarea.tng-textarea-field{padding-top:12px;padding-bottom:12px;resize:vertical;height:auto;line-height:1.5}.tng-form-field-subscript-wrapper{margin-top:4px;font-size:.75rem;display:flex;justify-content:space-between;padding:0 16px}.tng-form-field-spacer{flex:1 1 auto}.tng-form-field-hint{color:var(--tng-text-secondary, #666);text-align:right}\n"] }]
324
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], inputDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TngInputDirective), { isSignal: true }] }], textareaDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TngTextareaDirective), { isSignal: true }] }] } });
325
+
326
+ class TngTextareaComponent {
327
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
328
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
329
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
330
+ disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
331
+ maxLength = input(undefined, ...(ngDevMode ? [{ debugName: "maxLength" }] : []));
332
+ id = input(`tng-textarea-${Math.random().toString(36).substr(2, 9)}`, ...(ngDevMode ? [{ debugName: "id" }] : []));
333
+ value = signal('', ...(ngDevMode ? [{ debugName: "value" }] : []));
334
+ onChange = () => { };
335
+ onTouched = () => { };
336
+ onInput(event) {
337
+ const input = event.target;
338
+ this.value.set(input.value);
339
+ this.onChange(this.value());
340
+ }
341
+ onBlur() {
342
+ this.onTouched();
343
+ }
344
+ writeValue(value) {
345
+ this.value.set(value || '');
346
+ }
347
+ registerOnChange(fn) {
348
+ this.onChange = fn;
349
+ }
350
+ registerOnTouched(fn) {
351
+ this.onTouched = fn;
352
+ }
353
+ setDisabledState(isDisabled) {
354
+ this.disabled.update(() => isDisabled);
355
+ }
356
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: TngTextareaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
357
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.0", type: TngTextareaComponent, isStandalone: true, selector: "tng-textarea", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange" }, providers: [
358
+ {
359
+ provide: NG_VALUE_ACCESSOR,
360
+ useExisting: forwardRef(() => TngTextareaComponent),
361
+ multi: true
362
+ }
363
+ ], ngImport: i0, template: "<tng-form-field [label]=\"label()\">\n <textarea\n tngTextarea\n [id]=\"id()\"\n [placeholder]=\"placeholder()\"\n [value]=\"value()\"\n [disabled]=\"disabled()\"\n [maxLength]=\"maxLength()\"\n [required]=\"required()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\">\n </textarea>\n</tng-form-field>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: TngFormFieldComponent, selector: "tng-form-field", inputs: ["label"] }, { kind: "directive", type: TngTextareaDirective, selector: "textarea[tngTextarea]", inputs: ["disabled", "placeholder", "maxLength"], exportAs: ["tngTextarea"] }], encapsulation: i0.ViewEncapsulation.None });
364
+ }
365
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: TngTextareaComponent, decorators: [{
366
+ type: Component,
367
+ args: [{ selector: 'tng-textarea', standalone: true, imports: [CommonModule, FormsModule, TngFormFieldComponent, TngTextareaDirective], providers: [
368
+ {
369
+ provide: NG_VALUE_ACCESSOR,
370
+ useExisting: forwardRef(() => TngTextareaComponent),
371
+ multi: true
372
+ }
373
+ ], encapsulation: ViewEncapsulation.None, template: "<tng-form-field [label]=\"label()\">\n <textarea\n tngTextarea\n [id]=\"id()\"\n [placeholder]=\"placeholder()\"\n [value]=\"value()\"\n [disabled]=\"disabled()\"\n [maxLength]=\"maxLength()\"\n [required]=\"required()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\">\n </textarea>\n</tng-form-field>\n", styles: [":host{display:block}\n"] }]
374
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }] } });
250
375
 
251
376
  class TecnualDatepickerComponent {
252
377
  elementRef;
@@ -891,6 +1016,13 @@ class ThemeService {
891
1016
  description: 'Neon cyberpunk vibes',
892
1017
  primaryColor: '#00f2ff',
893
1018
  isDark: true
1019
+ },
1020
+ {
1021
+ name: 'futuristic-light',
1022
+ displayName: 'Futuristic Light',
1023
+ description: 'Clean cyberpunk aesthetic',
1024
+ primaryColor: '#00c2cc',
1025
+ isDark: false
894
1026
  }
895
1027
  ];
896
1028
  constructor() {
@@ -1490,6 +1622,9 @@ class TngSelectDirective {
1490
1622
  panelRef = null;
1491
1623
  // Generated trigger element
1492
1624
  triggerEl = null;
1625
+ // Disabled state tracking
1626
+ isDisabled = signal(false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
1627
+ mutationObserver = null;
1493
1628
  // Computed
1494
1629
  isMulti = computed(() => this.enableMulti() || this.multiple(), ...(ngDevMode ? [{ debugName: "isMulti" }] : []));
1495
1630
  options = computed(() => this._options(), ...(ngDevMode ? [{ debugName: "options" }] : []));
@@ -1527,13 +1662,27 @@ class TngSelectDirective {
1527
1662
  this.ngControl.control.setValue(newValue, { emitEvent: false });
1528
1663
  }
1529
1664
  });
1530
- // Update generated trigger text
1665
+ // Update generated trigger text and state
1531
1666
  effect(() => {
1532
1667
  if (this.triggerEl) {
1533
- const textSpan = this.triggerEl.querySelector('.tng-select-trigger-text');
1668
+ const textSpan = this.triggerEl.querySelector('.tng-select-display-text');
1534
1669
  if (textSpan) {
1535
1670
  textSpan.textContent = this.displayText();
1536
1671
  }
1672
+ // Toggle open class
1673
+ if (this.isOpen()) {
1674
+ this.triggerEl.classList.add('tng-select-open');
1675
+ }
1676
+ else {
1677
+ this.triggerEl.classList.remove('tng-select-open');
1678
+ }
1679
+ // Toggle disabled class
1680
+ if (this.isDisabled()) {
1681
+ this.triggerEl.classList.add('disabled');
1682
+ }
1683
+ else {
1684
+ this.triggerEl.classList.remove('disabled');
1685
+ }
1537
1686
  }
1538
1687
  });
1539
1688
  }
@@ -1546,6 +1695,16 @@ class TngSelectDirective {
1546
1695
  this.el.nativeElement.style.pointerEvents = 'none';
1547
1696
  this.el.nativeElement.style.height = '0';
1548
1697
  this.el.nativeElement.style.width = '0';
1698
+ // Track disabled state
1699
+ this.isDisabled.set(this.el.nativeElement.disabled);
1700
+ this.mutationObserver = new MutationObserver((mutations) => {
1701
+ mutations.forEach((mutation) => {
1702
+ if (mutation.attributeName === 'disabled') {
1703
+ this.isDisabled.set(this.el.nativeElement.disabled);
1704
+ }
1705
+ });
1706
+ });
1707
+ this.mutationObserver.observe(this.el.nativeElement, { attributes: true });
1549
1708
  if (!this.customTrigger()) {
1550
1709
  this.createTrigger();
1551
1710
  }
@@ -1554,7 +1713,9 @@ class TngSelectDirective {
1554
1713
  this.closePanel();
1555
1714
  if (this.triggerEl) {
1556
1715
  this.triggerEl.remove();
1716
+ this.triggerEl = null;
1557
1717
  }
1718
+ this.mutationObserver?.disconnect();
1558
1719
  }
1559
1720
  onClick(event) {
1560
1721
  // If we have a generated trigger, the click is handled there.
@@ -1575,30 +1736,17 @@ class TngSelectDirective {
1575
1736
  }
1576
1737
  createTrigger() {
1577
1738
  const trigger = document.createElement('div');
1578
- trigger.className = 'tng-select-trigger';
1579
- trigger.style.display = 'flex';
1580
- trigger.style.alignItems = 'center';
1581
- trigger.style.justifyContent = 'space-between';
1582
- trigger.style.padding = '0.75rem 1rem';
1583
- trigger.style.border = '1px solid #ccc';
1584
- trigger.style.borderRadius = '4px';
1585
- trigger.style.background = '#fff';
1586
- trigger.style.cursor = 'pointer';
1587
- trigger.style.minHeight = '48px';
1588
- trigger.style.boxSizing = 'border-box';
1739
+ trigger.className = 'tng-select-display';
1589
1740
  const text = document.createElement('span');
1590
- text.className = 'tng-select-trigger-text';
1741
+ text.className = 'tng-select-display-text';
1591
1742
  text.textContent = this.displayText();
1592
- text.style.flex = '1';
1593
- text.style.overflow = 'hidden';
1594
- text.style.textOverflow = 'ellipsis';
1595
- text.style.whiteSpace = 'nowrap';
1596
- const arrow = document.createElement('span');
1597
- arrow.innerHTML = '&#9662;'; // Down arrow
1598
- arrow.style.marginLeft = '0.5rem';
1743
+ const arrow = document.createElement('i');
1744
+ arrow.className = 'fa fa-chevron-down tng-select-arrow';
1599
1745
  trigger.appendChild(text);
1600
1746
  trigger.appendChild(arrow);
1601
1747
  trigger.addEventListener('click', (e) => {
1748
+ if (this.isDisabled())
1749
+ return;
1602
1750
  e.preventDefault();
1603
1751
  e.stopPropagation();
1604
1752
  this.togglePanel();
@@ -1881,7 +2029,7 @@ class TngSelectComponent {
1881
2029
  useExisting: forwardRef(() => TngSelectComponent),
1882
2030
  multi: true
1883
2031
  }
1884
- ], viewQueries: [{ propertyName: "selectDirective", first: true, predicate: TngSelectDirective, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"tng-select-wrapper\" [class.tng-select-open]=\"selectDirective()?.isOpen()\">\n @if (label()) {\n <label class=\"tng-select-label\" [attr.for]=\"selectId()\">\n {{ label() }}\n </label>\n }\n\n <div class=\"tng-select-container\">\n <select\n [id]=\"selectId()\"\n tngSelect\n [enableMulti]=\"enableMulti()\"\n [enableSearch]=\"enableSearch()\"\n [placeholder]=\"placeholder()\"\n [customTrigger]=\"true\"\n [multiple]=\"enableMulti()\"\n [triggerRef]=\"triggerDisplay\"\n [(selectedValues)]=\"value\"\n [disabled]=\"effectiveDisabled()\"\n [attr.aria-label]=\"ariaLabel() || label()\"\n >\n @for (option of options(); track option.value) {\n <option [value]=\"option.value\" [disabled]=\"option.disabled\">\n {{ option.label }}\n </option>\n }\n </select>\n\n <div\n class=\"tng-select-display\"\n #triggerDisplay\n [class.disabled]=\"effectiveDisabled()\"\n (click)=\"onDisplayClick($event)\"\n >\n <span class=\"tng-select-display-text\">\n {{ displayText() }}\n </span>\n <i class=\"fa fa-chevron-down tng-select-arrow\"></i>\n </div>\n </div>\n\n @if (hint()) {\n <div class=\"tng-select-hint\">\n {{ hint() }}\n </div>\n }\n</div>\n", styles: [":host{display:block;margin-bottom:1.5rem;font-family:var(--tng-font-family, \"Inter\", sans-serif)}.tng-select-wrapper{width:100%}.tng-select-label{display:block;margin-bottom:.5rem;font-size:.9rem;font-weight:500;color:var(--tng-text, #333)}.tng-select-container{position:relative}.tng-select-display{display:flex;align-items:center;justify-content:space-between;gap:.75rem;padding:.75rem 1rem;border:1px solid var(--tng-border, #999);border-radius:var(--tng-border-radius, 4px);background:var(--tng-surface, #ffffff);cursor:pointer;transition:all .2s ease;min-height:48px}.tng-select-display:hover:not(.disabled){border-color:var(--tng-text-secondary, #757575)}.tng-select-display.disabled{opacity:.6;cursor:not-allowed;background:var(--tng-background, #fafafa)}.tng-select-wrapper.tng-select-open .tng-select-display{border-color:var(--tng-primary, #00f2ff);border-width:2px;padding:calc(.75rem - 1px) calc(1rem - 1px)}.tng-select-display-text{flex:1;color:var(--tng-text, #333);font-size:1rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tng-select-display-text:empty:before{content:attr(data-placeholder);color:var(--tng-text-secondary, #757575)}.tng-select-arrow{color:var(--tng-text-secondary, #757575);transition:transform .2s ease;font-size:.8rem}.tng-select-wrapper.tng-select-open .tng-select-arrow{transform:rotate(180deg)}.tng-select-hint{margin-top:.25rem;font-size:.85rem;color:var(--tng-text-secondary, #757575)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: TngSelectDirective, selector: "select[tngSelect]", inputs: ["enableMulti", "multiple", "enableSearch", "placeholder", "customTrigger", "triggerRef", "selectedValues"], outputs: ["selectedValuesChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2032
+ ], viewQueries: [{ propertyName: "selectDirective", first: true, predicate: TngSelectDirective, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"tng-select-wrapper\" [class.tng-select-open]=\"selectDirective()?.isOpen()\">\n @if (label()) {\n <label class=\"tng-select-label\" [attr.for]=\"selectId()\">\n {{ label() }}\n </label>\n }\n\n <div class=\"tng-select-container\">\n <select\n [id]=\"selectId()\"\n tngSelect\n [enableMulti]=\"enableMulti()\"\n [enableSearch]=\"enableSearch()\"\n [placeholder]=\"placeholder()\"\n [customTrigger]=\"true\"\n [multiple]=\"enableMulti()\"\n [triggerRef]=\"triggerDisplay\"\n [(selectedValues)]=\"value\"\n [disabled]=\"effectiveDisabled()\"\n [attr.aria-label]=\"ariaLabel() || label()\"\n >\n @for (option of options(); track option.value) {\n <option [value]=\"option.value\" [disabled]=\"option.disabled\">\n {{ option.label }}\n </option>\n }\n </select>\n\n <div\n class=\"tng-select-display\"\n #triggerDisplay\n [class.disabled]=\"effectiveDisabled()\"\n (click)=\"onDisplayClick($event)\"\n >\n <span class=\"tng-select-display-text\">\n {{ displayText() }}\n </span>\n <i class=\"fa fa-chevron-down tng-select-arrow\"></i>\n </div>\n </div>\n\n @if (hint()) {\n <div class=\"tng-select-hint\">\n {{ hint() }}\n </div>\n }\n</div>\n", styles: [":host{display:block;margin-bottom:1.5rem;font-family:var(--tng-font-family, \"Inter\", sans-serif)}.tng-select-wrapper{width:100%}.tng-select-label{display:block;margin-bottom:.5rem;font-size:.9rem;font-weight:500;color:var(--tng-text, #333)}.tng-select-container{position:relative}.tng-select-display{display:flex;align-items:center;justify-content:space-between;gap:.75rem;padding:.75rem 1rem;border:1px solid var(--tng-border, #999);border-radius:var(--tng-border-radius, 4px);background:var(--tng-surface, #ffffff);cursor:pointer;transition:all .2s ease;min-height:48px;font-family:var(--tng-font-family, \"Inter\", sans-serif)}.tng-select-display:hover:not(.disabled){border-color:var(--tng-text-secondary, #757575)}.tng-select-display.disabled{opacity:.6;cursor:not-allowed;background:var(--tng-background, #fafafa)}.tng-select-wrapper.tng-select-open .tng-select-display,.tng-select-display.tng-select-open{border-color:var(--tng-primary, #00f2ff);border-width:2px;padding:calc(.75rem - 1px) calc(1rem - 1px)}.tng-select-display-text{flex:1;color:var(--tng-text, #333);font-size:1rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tng-select-display-text:empty:before{content:attr(data-placeholder);color:var(--tng-text-secondary, #757575)}.tng-select-arrow{color:var(--tng-text-secondary, #757575);transition:transform .2s ease;font-size:.8rem}.tng-select-wrapper.tng-select-open .tng-select-arrow,.tng-select-display.tng-select-open .tng-select-arrow{transform:rotate(180deg)}.tng-select-hint{margin-top:.25rem;font-size:.85rem;color:var(--tng-text-secondary, #757575)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: TngSelectDirective, selector: "select[tngSelect]", inputs: ["enableMulti", "multiple", "enableSearch", "placeholder", "customTrigger", "triggerRef", "selectedValues"], outputs: ["selectedValuesChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1885
2033
  }
1886
2034
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: TngSelectComponent, decorators: [{
1887
2035
  type: Component,
@@ -1891,9 +2039,166 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
1891
2039
  useExisting: forwardRef(() => TngSelectComponent),
1892
2040
  multi: true
1893
2041
  }
1894
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"tng-select-wrapper\" [class.tng-select-open]=\"selectDirective()?.isOpen()\">\n @if (label()) {\n <label class=\"tng-select-label\" [attr.for]=\"selectId()\">\n {{ label() }}\n </label>\n }\n\n <div class=\"tng-select-container\">\n <select\n [id]=\"selectId()\"\n tngSelect\n [enableMulti]=\"enableMulti()\"\n [enableSearch]=\"enableSearch()\"\n [placeholder]=\"placeholder()\"\n [customTrigger]=\"true\"\n [multiple]=\"enableMulti()\"\n [triggerRef]=\"triggerDisplay\"\n [(selectedValues)]=\"value\"\n [disabled]=\"effectiveDisabled()\"\n [attr.aria-label]=\"ariaLabel() || label()\"\n >\n @for (option of options(); track option.value) {\n <option [value]=\"option.value\" [disabled]=\"option.disabled\">\n {{ option.label }}\n </option>\n }\n </select>\n\n <div\n class=\"tng-select-display\"\n #triggerDisplay\n [class.disabled]=\"effectiveDisabled()\"\n (click)=\"onDisplayClick($event)\"\n >\n <span class=\"tng-select-display-text\">\n {{ displayText() }}\n </span>\n <i class=\"fa fa-chevron-down tng-select-arrow\"></i>\n </div>\n </div>\n\n @if (hint()) {\n <div class=\"tng-select-hint\">\n {{ hint() }}\n </div>\n }\n</div>\n", styles: [":host{display:block;margin-bottom:1.5rem;font-family:var(--tng-font-family, \"Inter\", sans-serif)}.tng-select-wrapper{width:100%}.tng-select-label{display:block;margin-bottom:.5rem;font-size:.9rem;font-weight:500;color:var(--tng-text, #333)}.tng-select-container{position:relative}.tng-select-display{display:flex;align-items:center;justify-content:space-between;gap:.75rem;padding:.75rem 1rem;border:1px solid var(--tng-border, #999);border-radius:var(--tng-border-radius, 4px);background:var(--tng-surface, #ffffff);cursor:pointer;transition:all .2s ease;min-height:48px}.tng-select-display:hover:not(.disabled){border-color:var(--tng-text-secondary, #757575)}.tng-select-display.disabled{opacity:.6;cursor:not-allowed;background:var(--tng-background, #fafafa)}.tng-select-wrapper.tng-select-open .tng-select-display{border-color:var(--tng-primary, #00f2ff);border-width:2px;padding:calc(.75rem - 1px) calc(1rem - 1px)}.tng-select-display-text{flex:1;color:var(--tng-text, #333);font-size:1rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tng-select-display-text:empty:before{content:attr(data-placeholder);color:var(--tng-text-secondary, #757575)}.tng-select-arrow{color:var(--tng-text-secondary, #757575);transition:transform .2s ease;font-size:.8rem}.tng-select-wrapper.tng-select-open .tng-select-arrow{transform:rotate(180deg)}.tng-select-hint{margin-top:.25rem;font-size:.85rem;color:var(--tng-text-secondary, #757575)}\n"] }]
2042
+ ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"tng-select-wrapper\" [class.tng-select-open]=\"selectDirective()?.isOpen()\">\n @if (label()) {\n <label class=\"tng-select-label\" [attr.for]=\"selectId()\">\n {{ label() }}\n </label>\n }\n\n <div class=\"tng-select-container\">\n <select\n [id]=\"selectId()\"\n tngSelect\n [enableMulti]=\"enableMulti()\"\n [enableSearch]=\"enableSearch()\"\n [placeholder]=\"placeholder()\"\n [customTrigger]=\"true\"\n [multiple]=\"enableMulti()\"\n [triggerRef]=\"triggerDisplay\"\n [(selectedValues)]=\"value\"\n [disabled]=\"effectiveDisabled()\"\n [attr.aria-label]=\"ariaLabel() || label()\"\n >\n @for (option of options(); track option.value) {\n <option [value]=\"option.value\" [disabled]=\"option.disabled\">\n {{ option.label }}\n </option>\n }\n </select>\n\n <div\n class=\"tng-select-display\"\n #triggerDisplay\n [class.disabled]=\"effectiveDisabled()\"\n (click)=\"onDisplayClick($event)\"\n >\n <span class=\"tng-select-display-text\">\n {{ displayText() }}\n </span>\n <i class=\"fa fa-chevron-down tng-select-arrow\"></i>\n </div>\n </div>\n\n @if (hint()) {\n <div class=\"tng-select-hint\">\n {{ hint() }}\n </div>\n }\n</div>\n", styles: [":host{display:block;margin-bottom:1.5rem;font-family:var(--tng-font-family, \"Inter\", sans-serif)}.tng-select-wrapper{width:100%}.tng-select-label{display:block;margin-bottom:.5rem;font-size:.9rem;font-weight:500;color:var(--tng-text, #333)}.tng-select-container{position:relative}.tng-select-display{display:flex;align-items:center;justify-content:space-between;gap:.75rem;padding:.75rem 1rem;border:1px solid var(--tng-border, #999);border-radius:var(--tng-border-radius, 4px);background:var(--tng-surface, #ffffff);cursor:pointer;transition:all .2s ease;min-height:48px;font-family:var(--tng-font-family, \"Inter\", sans-serif)}.tng-select-display:hover:not(.disabled){border-color:var(--tng-text-secondary, #757575)}.tng-select-display.disabled{opacity:.6;cursor:not-allowed;background:var(--tng-background, #fafafa)}.tng-select-wrapper.tng-select-open .tng-select-display,.tng-select-display.tng-select-open{border-color:var(--tng-primary, #00f2ff);border-width:2px;padding:calc(.75rem - 1px) calc(1rem - 1px)}.tng-select-display-text{flex:1;color:var(--tng-text, #333);font-size:1rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tng-select-display-text:empty:before{content:attr(data-placeholder);color:var(--tng-text-secondary, #757575)}.tng-select-arrow{color:var(--tng-text-secondary, #757575);transition:transform .2s ease;font-size:.8rem}.tng-select-wrapper.tng-select-open .tng-select-arrow,.tng-select-display.tng-select-open .tng-select-arrow{transform:rotate(180deg)}.tng-select-hint{margin-top:.25rem;font-size:.85rem;color:var(--tng-text-secondary, #757575)}\n"] }]
1895
2043
  }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], enableMulti: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableMulti", required: false }] }], enableSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableSearch", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], selectDirective: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TngSelectDirective), { isSignal: true }] }] } });
1896
2044
 
2045
+ class TngSliderComponent {
2046
+ // Inputs
2047
+ min = input(0, ...(ngDevMode ? [{ debugName: "min" }] : []));
2048
+ max = input(100, ...(ngDevMode ? [{ debugName: "max" }] : []));
2049
+ step = input(1, ...(ngDevMode ? [{ debugName: "step" }] : []));
2050
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
2051
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
2052
+ orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : []));
2053
+ // Internal state
2054
+ value = model(0, ...(ngDevMode ? [{ debugName: "value" }] : []));
2055
+ isDragging = signal(false, ...(ngDevMode ? [{ debugName: "isDragging" }] : []));
2056
+ // Elements
2057
+ track = viewChild.required('track');
2058
+ // Computed
2059
+ percentage = computed(() => {
2060
+ const min = this.min();
2061
+ const max = this.max();
2062
+ const val = this.value();
2063
+ if (max === min)
2064
+ return 0;
2065
+ const percent = ((val - min) / (max - min)) * 100;
2066
+ return Math.min(Math.max(percent, 0), 100);
2067
+ }, ...(ngDevMode ? [{ debugName: "percentage" }] : []));
2068
+ // CVA callbacks
2069
+ onChange = () => { };
2070
+ onTouched = () => { };
2071
+ constructor() {
2072
+ // Ensure value respects min/max/step when inputs change
2073
+ effect(() => {
2074
+ const val = this.value();
2075
+ const clamped = this.clampAndStep(val);
2076
+ if (val !== clamped) {
2077
+ this.value.set(clamped);
2078
+ }
2079
+ }, { allowSignalWrites: true });
2080
+ }
2081
+ // CVA Implementation
2082
+ writeValue(value) {
2083
+ if (value !== null && value !== undefined) {
2084
+ this.value.set(this.clampAndStep(value));
2085
+ }
2086
+ }
2087
+ registerOnChange(fn) {
2088
+ this.onChange = fn;
2089
+ }
2090
+ registerOnTouched(fn) {
2091
+ this.onTouched = fn;
2092
+ }
2093
+ setDisabledState(isDisabled) {
2094
+ // We handle disabled state via input, but CVA can also set it
2095
+ // Usually we'd sync this with a signal if we wanted to support both
2096
+ }
2097
+ // Interaction Logic
2098
+ onStart(event) {
2099
+ if (this.disabled())
2100
+ return;
2101
+ this.isDragging.set(true);
2102
+ this.onTouched();
2103
+ this.updateValueFromEvent(event);
2104
+ // Prevent text selection while dragging
2105
+ event.preventDefault();
2106
+ }
2107
+ onMove(event) {
2108
+ if (!this.isDragging() || this.disabled())
2109
+ return;
2110
+ this.updateValueFromEvent(event);
2111
+ }
2112
+ onEnd() {
2113
+ this.isDragging.set(false);
2114
+ }
2115
+ updateValueFromEvent(event) {
2116
+ const rect = this.track().nativeElement.getBoundingClientRect();
2117
+ let percent = 0;
2118
+ if (this.orientation() === 'horizontal') {
2119
+ const clientX = this.getClientX(event);
2120
+ percent = (clientX - rect.left) / rect.width;
2121
+ }
2122
+ else {
2123
+ const clientY = this.getClientY(event);
2124
+ // In vertical mode, 0 is at bottom, so we invert logic or CSS
2125
+ // Typically slider goes bottom->top.
2126
+ // clientY is 0 at top.
2127
+ // So percent = (rect.bottom - clientY) / rect.height
2128
+ percent = (rect.bottom - clientY) / rect.height;
2129
+ }
2130
+ percent = Math.min(Math.max(percent, 0), 1);
2131
+ const range = this.max() - this.min();
2132
+ const rawValue = this.min() + percent * range;
2133
+ const newValue = this.clampAndStep(rawValue);
2134
+ if (this.value() !== newValue) {
2135
+ this.value.set(newValue);
2136
+ this.onChange(newValue);
2137
+ }
2138
+ }
2139
+ getClientX(event) {
2140
+ if (event instanceof MouseEvent) {
2141
+ return event.clientX;
2142
+ }
2143
+ return event.touches[0].clientX;
2144
+ }
2145
+ getClientY(event) {
2146
+ if (event instanceof MouseEvent) {
2147
+ return event.clientY;
2148
+ }
2149
+ return event.touches[0].clientY;
2150
+ }
2151
+ clampAndStep(value) {
2152
+ const min = this.min();
2153
+ const max = this.max();
2154
+ const step = this.step();
2155
+ let clamped = Math.min(Math.max(value, min), max);
2156
+ if (step > 0) {
2157
+ const steps = Math.round((clamped - min) / step);
2158
+ clamped = min + steps * step;
2159
+ // Re-clamp to handle floating point issues or partial steps at max
2160
+ clamped = Math.min(Math.max(clamped, min), max);
2161
+ }
2162
+ return clamped;
2163
+ }
2164
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: TngSliderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2165
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: TngSliderComponent, isStandalone: true, selector: "tng-slider", inputs: { min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { listeners: { "mousedown": "onStart($event)", "touchstart": "onStart($event)", "window:mousemove": "onMove($event)", "window:touchmove": "onMove($event)", "window:mouseup": "onEnd()", "window:touchend": "onEnd()" } }, providers: [
2166
+ {
2167
+ provide: NG_VALUE_ACCESSOR,
2168
+ useExisting: forwardRef(() => TngSliderComponent),
2169
+ multi: true,
2170
+ },
2171
+ ], viewQueries: [{ propertyName: "track", first: true, predicate: ["track"], descendants: true, isSignal: true }], ngImport: i0, template: "<div \n class=\"tng-slider-container\" \n [class.disabled]=\"disabled()\"\n [class.dragging]=\"isDragging()\"\n [class.vertical]=\"orientation() === 'vertical'\">\n \n @if (label()) {\n <label class=\"tng-slider-label\">{{ label() }}</label>\n }\n\n <div class=\"tng-slider-wrapper\">\n <div class=\"tng-slider-track\" #track>\n <div \n class=\"tng-slider-fill\" \n [style.width.%]=\"orientation() === 'horizontal' ? percentage() : 100\"\n [style.height.%]=\"orientation() === 'vertical' ? percentage() : 100\">\n </div>\n <div \n class=\"tng-slider-thumb\" \n [style.left.%]=\"orientation() === 'horizontal' ? percentage() : 50\"\n [style.bottom.%]=\"orientation() === 'vertical' ? percentage() : undefined\">\n <div class=\"tng-slider-thumb-knob\"></div>\n <div class=\"tng-slider-thumb-label\">{{ value() }}</div>\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{display:block;width:100%}.tng-slider-container{display:flex;flex-direction:column;gap:.5rem;padding:1rem 0;-webkit-user-select:none;user-select:none}.tng-slider-container.disabled{opacity:.5;pointer-events:none;filter:grayscale(1)}.tng-slider-label{font-family:inherit;font-size:.875rem;font-weight:500;color:var(--tng-text);margin-bottom:.25rem}.tng-slider-wrapper{position:relative;height:24px;display:flex;align-items:center;cursor:pointer;touch-action:none}.tng-slider-track{position:relative;width:100%;height:6px;background:rgba(var(--tng-primary),.2);background:color-mix(in srgb,var(--tng-primary),transparent 80%);border-radius:3px;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);overflow:visible;border:1px solid color-mix(in srgb,var(--tng-primary),transparent 90%);box-shadow:inset 0 1px 3px #0003}.tng-slider-fill{position:absolute;top:0;left:0;height:100%;background:linear-gradient(90deg,var(--tng-primary),var(--tng-secondary));border-radius:3px;transition:width .2s cubic-bezier(.4,0,.2,1);box-shadow:0 0 10px color-mix(in srgb,var(--tng-primary),transparent 50%)}.tng-slider-thumb{position:absolute;top:50%;transform:translate(-50%,-50%);width:24px;height:24px;display:flex;align-items:center;justify-content:center;z-index:2;transition:left .2s cubic-bezier(.4,0,.2,1)}.tng-slider-thumb-knob{width:20px;height:20px;background:var(--tng-surface);border:2px solid var(--tng-primary);border-radius:50%;box-shadow:0 2px 5px #0000004d;transition:transform .1s ease,box-shadow .2s ease}.tng-slider-thumb-knob:after{content:\"\";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:8px;height:8px;background:var(--tng-primary);border-radius:50%;opacity:.5}.tng-slider-thumb-label{position:absolute;top:-35px;left:50%;transform:translate(-50%) scale(0);background:color-mix(in srgb,var(--tng-surface),transparent 10%);color:var(--tng-primary);padding:4px 8px;border-radius:6px;font-size:.75rem;font-weight:700;border:1px solid color-mix(in srgb,var(--tng-primary),transparent 70%);pointer-events:none;transition:transform .2s cubic-bezier(.175,.885,.32,1.275),opacity .2s ease;opacity:0;box-shadow:0 4px 10px #0003;white-space:nowrap}.tng-slider-container:hover .tng-slider-thumb-knob{box-shadow:0 0 15px color-mix(in srgb,var(--tng-primary),transparent 60%)}.tng-slider-container:hover .tng-slider-thumb-label,.tng-slider-container.dragging .tng-slider-thumb-label{transform:translate(-50%) scale(1);opacity:1}.tng-slider-container.dragging .tng-slider-fill,.tng-slider-container.dragging .tng-slider-thumb{transition:none}.tng-slider-container.dragging .tng-slider-thumb-knob{transform:scale(1.1);background:var(--tng-primary);border-color:var(--tng-text);box-shadow:0 0 20px color-mix(in srgb,var(--tng-primary),transparent 40%)}.tng-slider-container.dragging .tng-slider-thumb-knob:after{background:var(--tng-text);opacity:1}.tng-slider-container.dragging .tng-slider-track{cursor:grabbing}.tng-slider-container.vertical{height:100%;min-height:150px;flex-direction:column;align-items:center}.tng-slider-container.vertical .tng-slider-wrapper{width:24px;height:100%;flex-direction:column}.tng-slider-container.vertical .tng-slider-track{width:6px;height:100%}.tng-slider-container.vertical .tng-slider-fill{width:100%;bottom:0;top:auto;transition:height .2s cubic-bezier(.4,0,.2,1)}.tng-slider-container.vertical .tng-slider-thumb{top:auto;left:50%;transform:translate(-50%,50%);transition:bottom .2s cubic-bezier(.4,0,.2,1)}.tng-slider-container.vertical .tng-slider-thumb-label{top:50%;left:35px;transform:translateY(-50%) scale(0)}.tng-slider-container.vertical:hover .tng-slider-thumb-label,.tng-slider-container.vertical.dragging .tng-slider-thumb-label{transform:translateY(-50%) scale(1)}.tng-slider-container.vertical.dragging .tng-slider-fill,.tng-slider-container.vertical.dragging .tng-slider-thumb{transition:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
2172
+ }
2173
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: TngSliderComponent, decorators: [{
2174
+ type: Component,
2175
+ args: [{ selector: 'tng-slider', standalone: true, imports: [CommonModule], providers: [
2176
+ {
2177
+ provide: NG_VALUE_ACCESSOR,
2178
+ useExisting: forwardRef(() => TngSliderComponent),
2179
+ multi: true,
2180
+ },
2181
+ ], template: "<div \n class=\"tng-slider-container\" \n [class.disabled]=\"disabled()\"\n [class.dragging]=\"isDragging()\"\n [class.vertical]=\"orientation() === 'vertical'\">\n \n @if (label()) {\n <label class=\"tng-slider-label\">{{ label() }}</label>\n }\n\n <div class=\"tng-slider-wrapper\">\n <div class=\"tng-slider-track\" #track>\n <div \n class=\"tng-slider-fill\" \n [style.width.%]=\"orientation() === 'horizontal' ? percentage() : 100\"\n [style.height.%]=\"orientation() === 'vertical' ? percentage() : 100\">\n </div>\n <div \n class=\"tng-slider-thumb\" \n [style.left.%]=\"orientation() === 'horizontal' ? percentage() : 50\"\n [style.bottom.%]=\"orientation() === 'vertical' ? percentage() : undefined\">\n <div class=\"tng-slider-thumb-knob\"></div>\n <div class=\"tng-slider-thumb-label\">{{ value() }}</div>\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{display:block;width:100%}.tng-slider-container{display:flex;flex-direction:column;gap:.5rem;padding:1rem 0;-webkit-user-select:none;user-select:none}.tng-slider-container.disabled{opacity:.5;pointer-events:none;filter:grayscale(1)}.tng-slider-label{font-family:inherit;font-size:.875rem;font-weight:500;color:var(--tng-text);margin-bottom:.25rem}.tng-slider-wrapper{position:relative;height:24px;display:flex;align-items:center;cursor:pointer;touch-action:none}.tng-slider-track{position:relative;width:100%;height:6px;background:rgba(var(--tng-primary),.2);background:color-mix(in srgb,var(--tng-primary),transparent 80%);border-radius:3px;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);overflow:visible;border:1px solid color-mix(in srgb,var(--tng-primary),transparent 90%);box-shadow:inset 0 1px 3px #0003}.tng-slider-fill{position:absolute;top:0;left:0;height:100%;background:linear-gradient(90deg,var(--tng-primary),var(--tng-secondary));border-radius:3px;transition:width .2s cubic-bezier(.4,0,.2,1);box-shadow:0 0 10px color-mix(in srgb,var(--tng-primary),transparent 50%)}.tng-slider-thumb{position:absolute;top:50%;transform:translate(-50%,-50%);width:24px;height:24px;display:flex;align-items:center;justify-content:center;z-index:2;transition:left .2s cubic-bezier(.4,0,.2,1)}.tng-slider-thumb-knob{width:20px;height:20px;background:var(--tng-surface);border:2px solid var(--tng-primary);border-radius:50%;box-shadow:0 2px 5px #0000004d;transition:transform .1s ease,box-shadow .2s ease}.tng-slider-thumb-knob:after{content:\"\";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:8px;height:8px;background:var(--tng-primary);border-radius:50%;opacity:.5}.tng-slider-thumb-label{position:absolute;top:-35px;left:50%;transform:translate(-50%) scale(0);background:color-mix(in srgb,var(--tng-surface),transparent 10%);color:var(--tng-primary);padding:4px 8px;border-radius:6px;font-size:.75rem;font-weight:700;border:1px solid color-mix(in srgb,var(--tng-primary),transparent 70%);pointer-events:none;transition:transform .2s cubic-bezier(.175,.885,.32,1.275),opacity .2s ease;opacity:0;box-shadow:0 4px 10px #0003;white-space:nowrap}.tng-slider-container:hover .tng-slider-thumb-knob{box-shadow:0 0 15px color-mix(in srgb,var(--tng-primary),transparent 60%)}.tng-slider-container:hover .tng-slider-thumb-label,.tng-slider-container.dragging .tng-slider-thumb-label{transform:translate(-50%) scale(1);opacity:1}.tng-slider-container.dragging .tng-slider-fill,.tng-slider-container.dragging .tng-slider-thumb{transition:none}.tng-slider-container.dragging .tng-slider-thumb-knob{transform:scale(1.1);background:var(--tng-primary);border-color:var(--tng-text);box-shadow:0 0 20px color-mix(in srgb,var(--tng-primary),transparent 40%)}.tng-slider-container.dragging .tng-slider-thumb-knob:after{background:var(--tng-text);opacity:1}.tng-slider-container.dragging .tng-slider-track{cursor:grabbing}.tng-slider-container.vertical{height:100%;min-height:150px;flex-direction:column;align-items:center}.tng-slider-container.vertical .tng-slider-wrapper{width:24px;height:100%;flex-direction:column}.tng-slider-container.vertical .tng-slider-track{width:6px;height:100%}.tng-slider-container.vertical .tng-slider-fill{width:100%;bottom:0;top:auto;transition:height .2s cubic-bezier(.4,0,.2,1)}.tng-slider-container.vertical .tng-slider-thumb{top:auto;left:50%;transform:translate(-50%,50%);transition:bottom .2s cubic-bezier(.4,0,.2,1)}.tng-slider-container.vertical .tng-slider-thumb-label{top:50%;left:35px;transform:translateY(-50%) scale(0)}.tng-slider-container.vertical:hover .tng-slider-thumb-label,.tng-slider-container.vertical.dragging .tng-slider-thumb-label{transform:translateY(-50%) scale(1)}.tng-slider-container.vertical.dragging .tng-slider-fill,.tng-slider-container.vertical.dragging .tng-slider-thumb{transition:none}\n"] }]
2182
+ }], ctorParameters: () => [], propDecorators: { min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], track: [{ type: i0.ViewChild, args: ['track', { isSignal: true }] }], onStart: [{
2183
+ type: HostListener,
2184
+ args: ['mousedown', ['$event']]
2185
+ }, {
2186
+ type: HostListener,
2187
+ args: ['touchstart', ['$event']]
2188
+ }], onMove: [{
2189
+ type: HostListener,
2190
+ args: ['window:mousemove', ['$event']]
2191
+ }, {
2192
+ type: HostListener,
2193
+ args: ['window:touchmove', ['$event']]
2194
+ }], onEnd: [{
2195
+ type: HostListener,
2196
+ args: ['window:mouseup']
2197
+ }, {
2198
+ type: HostListener,
2199
+ args: ['window:touchend']
2200
+ }] } });
2201
+
1897
2202
  /*
1898
2203
  * Public API Surface of tecnualng
1899
2204
  */
@@ -1902,5 +2207,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
1902
2207
  * Generated bundle index. Do not edit.
1903
2208
  */
1904
2209
 
1905
- export { TecnualDatepickerComponent, TecnualInputComponent, TecnualTableComponent, ThemeService, TngButton, TngCardComponent, TngExpansionPanelComponent, TngFormFieldComponent, TngInputDirective, TngMenuComponent, TngMenuGroupComponent, TngMenuItemComponent, TngSelectComponent, TngSelectDirective, TngSelectPanelComponent, TngSidebarComponent, TngTabComponent, TngTabsComponent, TngToolbarComponent, TngTooltipComponent, TngTooltipDirective };
2210
+ export { TecnualDatepickerComponent, TecnualInputComponent, TecnualTableComponent, ThemeService, TngButton, TngCardComponent, TngExpansionPanelComponent, TngFormFieldComponent, TngInputDirective, TngMenuComponent, TngMenuGroupComponent, TngMenuItemComponent, TngSelectComponent, TngSelectDirective, TngSelectPanelComponent, TngSidebarComponent, TngSliderComponent, TngTabComponent, TngTabsComponent, TngTextareaComponent, TngTextareaDirective, TngToolbarComponent, TngTooltipComponent, TngTooltipDirective };
1906
2211
  //# sourceMappingURL=tecnualng.mjs.map