@brickclay-org/ui 0.0.16 → 0.0.17

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 CHANGED
@@ -59,7 +59,7 @@ _More components coming soon..._
59
59
  ## Installation
60
60
 
61
61
  ```bash
62
- npm i @brickclay-org/ui@0.0.16
62
+ npm i @brickclay-org/ui@0.0.17
63
63
  ```
64
64
 
65
65
  ### Peer Dependencies
@@ -1,7 +1,8 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Component, EventEmitter, HostListener, ViewChildren, Output, Input, Injectable, NgModule, forwardRef, ViewEncapsulation } from '@angular/core';
2
+ import { Component, EventEmitter, HostListener, ViewChildren, Output, Input, Injectable, NgModule, forwardRef, ViewEncapsulation, Optional, Self } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
+ import * as i1$1 from '@angular/forms';
5
6
  import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
6
7
  import moment from 'moment';
7
8
  import { Subject } from 'rxjs';
@@ -2748,6 +2749,136 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2748
2749
  type: Output
2749
2750
  }] } });
2750
2751
 
2752
+ class Textarea {
2753
+ ngControl;
2754
+ name;
2755
+ id;
2756
+ label = '';
2757
+ placeholder = '';
2758
+ rows = 4;
2759
+ hint = '';
2760
+ required = false;
2761
+ maxLength = null;
2762
+ hasError = false;
2763
+ errorMessage = '';
2764
+ trimWhiteSpaces = false;
2765
+ eventType = 'input';
2766
+ value = '';
2767
+ isDisabled = false;
2768
+ // --- ControlValueAccessor ---
2769
+ onChange = (_) => { };
2770
+ onTouched = () => { };
2771
+ constructor(ngControl) {
2772
+ this.ngControl = ngControl;
2773
+ if (this.ngControl) {
2774
+ this.ngControl.valueAccessor = this;
2775
+ }
2776
+ }
2777
+ // --- Computed ID ---
2778
+ get effectiveId() {
2779
+ const base = 'brickclay_textarea_';
2780
+ return this.id ? `${base}${this.id}` : `${base}${this.label?.replace(/\s+/g, '_').toLowerCase()}`;
2781
+ }
2782
+ // --- Expose FormControl state ---
2783
+ get control() {
2784
+ return this.ngControl?.control;
2785
+ }
2786
+ get touched() {
2787
+ return this.control?.touched;
2788
+ }
2789
+ get dirty() {
2790
+ return this.control?.dirty;
2791
+ }
2792
+ get errors() {
2793
+ return this.control?.errors;
2794
+ }
2795
+ get showError() {
2796
+ debugger;
2797
+ if (this.hasError)
2798
+ return true;
2799
+ if (!this.control)
2800
+ return false;
2801
+ return (this.required && (this.control.dirty || this.control.touched) &&
2802
+ this.control.invalid &&
2803
+ this.control.errors?.['required']);
2804
+ }
2805
+ // --- Event handler ---
2806
+ handleEvent(event) {
2807
+ const input = event.target;
2808
+ // Always update internal value for the counter
2809
+ this.value = input.value;
2810
+ // Trim spaces if needed
2811
+ if (this.trimWhiteSpaces && this.eventType === 'input') {
2812
+ setTimeout(() => {
2813
+ this.value = this.value.trim();
2814
+ input.value = this.value;
2815
+ if (this.eventType === 'input')
2816
+ this.onChange(this.value);
2817
+ }, 300); // small delay for input event
2818
+ }
2819
+ else if (this.trimWhiteSpaces) {
2820
+ this.value = this.value.trim();
2821
+ input.value = this.value;
2822
+ }
2823
+ // Update ngModel depending on event type
2824
+ if (this.eventType === 'input' && event.type === 'input')
2825
+ this.onChange(this.value);
2826
+ if (this.eventType === 'change' && event.type === 'change')
2827
+ this.onChange(this.value);
2828
+ if (this.eventType === 'blur' && event.type === 'blur')
2829
+ this.onChange(this.value);
2830
+ // Always mark as touched on blur/focus
2831
+ if (event.type === 'blur' || event.type === 'focus')
2832
+ this.onTouched();
2833
+ }
2834
+ writeValue(value) {
2835
+ this.value = value ?? '';
2836
+ }
2837
+ registerOnChange(fn) {
2838
+ this.onChange = fn;
2839
+ }
2840
+ registerOnTouched(fn) {
2841
+ this.onTouched = fn;
2842
+ }
2843
+ setDisabledState(isDisabled) {
2844
+ this.isDisabled = isDisabled;
2845
+ }
2846
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: Textarea, deps: [{ token: i1$1.NgControl, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Component });
2847
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: Textarea, isStandalone: true, selector: "brickclay-textarea", inputs: { name: "name", id: "id", label: "label", placeholder: "placeholder", rows: "rows", hint: "hint", required: "required", maxLength: "maxLength", hasError: "hasError", errorMessage: "errorMessage", trimWhiteSpaces: "trimWhiteSpaces", eventType: "eventType" }, ngImport: i0, template: "<div class=\"flex flex-col gap-1.5 w-full\">\r\n\r\n @if (label) {\r\n <label\r\n class=\"text-sm font-medium text-[#141414] block\" [attr.for]=\"effectiveId\">\r\n {{ label }}\r\n @if (required) {\r\n <span class=\"text-[#E7000B] ml-0.5\">*</span>\r\n }\r\n </label>\r\n }\r\n\r\n <div class=\"relative\">\r\n <textarea\r\n [id]=\"effectiveId\"\r\n [attr.name]=\"name\"\r\n [value]=\"value\"\r\n (input)=\"handleEvent($event)\"\r\n (change)=\"handleEvent($event)\"\r\n (blur)=\"handleEvent($event)\"\r\n (focus)=\"handleEvent($event)\"\r\n [placeholder]=\"placeholder\"\r\n [disabled]=\"isDisabled\"\r\n [attr.maxlength]=\"maxLength\"\r\n rows=\"{{rows}}\"\r\n class=\"\r\n w-full\r\n px-3 py-2.5\r\n text-sm\r\n border border-[#E3E3E7] rounded-[4px]\r\n outline-none\r\n transition-colors duration-200\r\n bg-white resize-y\r\n placeholder:text-[#6B7080]\r\n \"\r\n [ngClass]=\"{\r\n 'border-[#FA727A] text-[#141414]': hasError && !isDisabled,\r\n\r\n 'focus:border-[#6B7080] text-[#141414]': !hasError && !isDisabled,\r\n\r\n 'bg-[#F4F4F6] text-[#A1A3AE] border-[#E3E3E7] cursor-not-allowed': isDisabled\r\n }\"\r\n ></textarea>\r\n </div>\r\n\r\n <div class=\"flex justify-between items-start font-normal text-sm\">\r\n\r\n <div class=\"flex-1\">\r\n @if (showError) {\r\n <span class=\"text-[#F34050]\">\r\n {{ errorMessage }}\r\n </span>\r\n } @else if (hint) {\r\n <span class=\"text-[#868997]\">\r\n {{ hint }}\r\n </span>\r\n }\r\n </div>\r\n\r\n @if (maxLength) {\r\n <div\r\n class=\"text-[#868997] tabular-nums flex-shrink-0\"\r\n >\r\n {{ value.length }}/{{ maxLength }}\r\n </div>\r\n }\r\n\r\n\r\n </div>\r\n\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }] });
2848
+ }
2849
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: Textarea, decorators: [{
2850
+ type: Component,
2851
+ args: [{ selector: 'brickclay-textarea', standalone: true, imports: [CommonModule, FormsModule], template: "<div class=\"flex flex-col gap-1.5 w-full\">\r\n\r\n @if (label) {\r\n <label\r\n class=\"text-sm font-medium text-[#141414] block\" [attr.for]=\"effectiveId\">\r\n {{ label }}\r\n @if (required) {\r\n <span class=\"text-[#E7000B] ml-0.5\">*</span>\r\n }\r\n </label>\r\n }\r\n\r\n <div class=\"relative\">\r\n <textarea\r\n [id]=\"effectiveId\"\r\n [attr.name]=\"name\"\r\n [value]=\"value\"\r\n (input)=\"handleEvent($event)\"\r\n (change)=\"handleEvent($event)\"\r\n (blur)=\"handleEvent($event)\"\r\n (focus)=\"handleEvent($event)\"\r\n [placeholder]=\"placeholder\"\r\n [disabled]=\"isDisabled\"\r\n [attr.maxlength]=\"maxLength\"\r\n rows=\"{{rows}}\"\r\n class=\"\r\n w-full\r\n px-3 py-2.5\r\n text-sm\r\n border border-[#E3E3E7] rounded-[4px]\r\n outline-none\r\n transition-colors duration-200\r\n bg-white resize-y\r\n placeholder:text-[#6B7080]\r\n \"\r\n [ngClass]=\"{\r\n 'border-[#FA727A] text-[#141414]': hasError && !isDisabled,\r\n\r\n 'focus:border-[#6B7080] text-[#141414]': !hasError && !isDisabled,\r\n\r\n 'bg-[#F4F4F6] text-[#A1A3AE] border-[#E3E3E7] cursor-not-allowed': isDisabled\r\n }\"\r\n ></textarea>\r\n </div>\r\n\r\n <div class=\"flex justify-between items-start font-normal text-sm\">\r\n\r\n <div class=\"flex-1\">\r\n @if (showError) {\r\n <span class=\"text-[#F34050]\">\r\n {{ errorMessage }}\r\n </span>\r\n } @else if (hint) {\r\n <span class=\"text-[#868997]\">\r\n {{ hint }}\r\n </span>\r\n }\r\n </div>\r\n\r\n @if (maxLength) {\r\n <div\r\n class=\"text-[#868997] tabular-nums flex-shrink-0\"\r\n >\r\n {{ value.length }}/{{ maxLength }}\r\n </div>\r\n }\r\n\r\n\r\n </div>\r\n\r\n</div>\r\n" }]
2852
+ }], ctorParameters: () => [{ type: i1$1.NgControl, decorators: [{
2853
+ type: Optional
2854
+ }, {
2855
+ type: Self
2856
+ }] }], propDecorators: { name: [{
2857
+ type: Input
2858
+ }], id: [{
2859
+ type: Input
2860
+ }], label: [{
2861
+ type: Input
2862
+ }], placeholder: [{
2863
+ type: Input
2864
+ }], rows: [{
2865
+ type: Input
2866
+ }], hint: [{
2867
+ type: Input
2868
+ }], required: [{
2869
+ type: Input
2870
+ }], maxLength: [{
2871
+ type: Input
2872
+ }], hasError: [{
2873
+ type: Input
2874
+ }], errorMessage: [{
2875
+ type: Input
2876
+ }], trimWhiteSpaces: [{
2877
+ type: Input
2878
+ }], eventType: [{
2879
+ type: Input
2880
+ }] } });
2881
+
2751
2882
  /*
2752
2883
  * Public API Surface of brickclay-lib
2753
2884
  */
@@ -2757,5 +2888,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2757
2888
  * Generated bundle index. Do not edit.
2758
2889
  */
2759
2890
 
2760
- export { BadgeComponent, BrickclayIcons, BrickclayLib, ButtonGroup, CalendarManagerService, CalendarModule, CheckboxComponent, CustomCalendarComponent, PillComponent, RadioComponent, ScheduledDatePickerComponent, Spinner, TimePickerComponent, ToggleComponent, UiButton, UiIconButton };
2891
+ export { BadgeComponent, BrickclayIcons, BrickclayLib, ButtonGroup, CalendarManagerService, CalendarModule, CheckboxComponent, CustomCalendarComponent, PillComponent, RadioComponent, ScheduledDatePickerComponent, Spinner, Textarea, TimePickerComponent, ToggleComponent, UiButton, UiIconButton };
2761
2892
  //# sourceMappingURL=brickclay-org-ui.mjs.map