@brickclay-org/ui 0.0.16 → 0.0.18

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,10 +1,14 @@
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, ViewChild } 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';
9
+ import * as i2 from '@angular/cdk/drag-drop';
10
+ import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
11
+ import { ScrollingModule } from '@angular/cdk/scrolling';
8
12
 
9
13
  // Icon paths that will be resolved relative to the library's assets folder
10
14
  // When published to npm, users will need to configure their angular.json to include these assets
@@ -2487,7 +2491,7 @@ class PillComponent {
2487
2491
  }
2488
2492
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: PillComponent, decorators: [{
2489
2493
  type: Component,
2490
- args: [{ selector: 'brickclay-pill', imports: [], template: "<span [className]=\"containerClasses\">\r\n\r\n @if (dot === 'left') {\r\n <span class=\"dot\"></span>\r\n }\r\n\r\n <span>{{ label }}</span>\r\n\r\n @if (dot === 'right') {\r\n <span class=\"dot\"></span>\r\n }\r\n\r\n @if (removable) {\r\n <button\r\n (click)=\"onRemove($event)\"\r\n class=\"pill-close \"> <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\r\n </svg>\r\n</button>\r\n }\r\n</span>\r\n", styles: [".pill{@apply inline-flex items-center justify-center font-medium border transition-colors duration-200 cursor-default gap-1.5 rounded-full;}.pill-xsm{@apply px-2 py-0.5 text-[10px] leading-[14px];}.pill-sm{@apply px-2 py-0.5 text-xs leading-[18px];}.pill-md{@apply px-2 py-0.5 text-sm;}.pill-lg{@apply px-3 py-1 text-sm;}.dot{@apply rounded-full size-1.5 flex-shrink-0 flex-grow-0 bg-current;}.pill-close{@apply flex items-center justify-center rounded-full text-inherit transition-colors;}.pill-xsm .pill-close,.pill-sm .pill-close{@apply w-3 h-3;}.pill-md .pill-close,.pill-lg .pill-close{@apply w-3.5 h-3.5;}.Gray-Solid{@apply bg-[#6B7080] border-[#6B7080] text-white;}.Primary-Solid{@apply bg-[#294FFF] border-[#294FFF] text-white;}.Error-Solid{@apply bg-[#FA727A] border-[#FA727A] text-[#4C0513];}.Warning-Solid{@apply bg-[#FA9E3A] border-[#FA9E3A] text-[#461C04];}.Success-Solid{@apply bg-[#57D175] border-[#57D175] text-[#461C04];}.Success-Solid .dot{@apply bg-[#082B13];}.Purple-Solid{@apply bg-[#CE8EF2] border-[#CE8EF2] text-[#461C04];}.Cyan-Solid{@apply bg-[#3FC2F1] border-[#3FC2F1] text-[#461C04];}.Gray-Light{@apply bg-[#F4F4F6] border-transparent text-[#363C51];}.Primary-Light{@apply bg-[#E5F3FF] border-transparent text-[#1434CB];}.Error-Light{@apply bg-[#FFF1F1] border-transparent text-[#CB1432];}.Warning-Light{@apply bg-[#FFEED7] border-transparent text-[#A04C02];}.Success-Light{@apply bg-[#F1FCF3] border-transparent text-[#1E7735];}.Purple-Light{@apply bg-[#F6EAFD] border-transparent text-[#9130C0];}.Cyan-Light{@apply bg-[#F1FAFE] border-transparent text-[#096E9B];}.Gray-Outline{@apply bg-[#F4F4F6] border-[#BBBDC5] text-[#363C51];}.Primary-Outline{@apply bg-[#E5F3FF] border-[#3F71FF] text-[#1434CB];}.Error-Outline{@apply bg-[#FFF1F1] border-[#FA727A] text-[#CB1432];}.Warning-Outline{@apply bg-[#FFEED7] border-[#FBAE58] text-[#A04C02];}.Success-Outline{@apply bg-[#F1FCF3] border-[#57D175] text-[#1E7735];}.Purple-Outline{@apply bg-[#F6EAFD] border-[#CE8EF2] text-[#9130C0];}.Cyan-Outline{@apply bg-[#F1FAFE] border-[#3FC2F1] text-[#096E9B];}.Gray-Transparent{@apply bg-transparent border-[#BBBDC5] text-[#363C51];}.Primary-Transparent{@apply bg-transparent border-[#3F71FF] text-[#1434CB];}.Error-Transparent{@apply bg-transparent border-[#FA727A] text-[#CB1432];}.Warning-Transparent{@apply bg-transparent border-[#FBAE58] text-[#A04C02];}.Success-Transparent{@apply bg-transparent border-[#57D175] text-[#1E7735];}.Purple-Transparent{@apply bg-transparent border-[#CE8EF2] text-[#9130C0];}.Cyan-Transparent{@apply bg-transparent border-[#3FC2F1] text-[#096E9B];}\n"] }]
2494
+ args: [{ selector: 'brickclay-pill', standalone: true, template: "<span [className]=\"containerClasses\">\r\n\r\n @if (dot === 'left') {\r\n <span class=\"dot\"></span>\r\n }\r\n\r\n <span>{{ label }}</span>\r\n\r\n @if (dot === 'right') {\r\n <span class=\"dot\"></span>\r\n }\r\n\r\n @if (removable) {\r\n <button\r\n (click)=\"onRemove($event)\"\r\n class=\"pill-close \"> <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\r\n </svg>\r\n</button>\r\n }\r\n</span>\r\n", styles: [".pill{@apply inline-flex items-center justify-center font-medium border transition-colors duration-200 cursor-default gap-1.5 rounded-full;}.pill-xsm{@apply px-2 py-0.5 text-[10px] leading-[14px];}.pill-sm{@apply px-2 py-0.5 text-xs leading-[18px];}.pill-md{@apply px-2 py-0.5 text-sm;}.pill-lg{@apply px-3 py-1 text-sm;}.dot{@apply rounded-full size-1.5 flex-shrink-0 flex-grow-0 bg-current;}.pill-close{@apply flex items-center justify-center rounded-full text-inherit transition-colors;}.pill-xsm .pill-close,.pill-sm .pill-close{@apply w-3 h-3;}.pill-md .pill-close,.pill-lg .pill-close{@apply w-3.5 h-3.5;}.Gray-Solid{@apply bg-[#6B7080] border-[#6B7080] text-white;}.Primary-Solid{@apply bg-[#294FFF] border-[#294FFF] text-white;}.Error-Solid{@apply bg-[#FA727A] border-[#FA727A] text-[#4C0513];}.Warning-Solid{@apply bg-[#FA9E3A] border-[#FA9E3A] text-[#461C04];}.Success-Solid{@apply bg-[#57D175] border-[#57D175] text-[#461C04];}.Success-Solid .dot{@apply bg-[#082B13];}.Purple-Solid{@apply bg-[#CE8EF2] border-[#CE8EF2] text-[#461C04];}.Cyan-Solid{@apply bg-[#3FC2F1] border-[#3FC2F1] text-[#461C04];}.Gray-Light{@apply bg-[#F4F4F6] border-transparent text-[#363C51];}.Primary-Light{@apply bg-[#E5F3FF] border-transparent text-[#1434CB];}.Error-Light{@apply bg-[#FFF1F1] border-transparent text-[#CB1432];}.Warning-Light{@apply bg-[#FFEED7] border-transparent text-[#A04C02];}.Success-Light{@apply bg-[#F1FCF3] border-transparent text-[#1E7735];}.Purple-Light{@apply bg-[#F6EAFD] border-transparent text-[#9130C0];}.Cyan-Light{@apply bg-[#F1FAFE] border-transparent text-[#096E9B];}.Gray-Outline{@apply bg-[#F4F4F6] border-[#BBBDC5] text-[#363C51];}.Primary-Outline{@apply bg-[#E5F3FF] border-[#3F71FF] text-[#1434CB];}.Error-Outline{@apply bg-[#FFF1F1] border-[#FA727A] text-[#CB1432];}.Warning-Outline{@apply bg-[#FFEED7] border-[#FBAE58] text-[#A04C02];}.Success-Outline{@apply bg-[#F1FCF3] border-[#57D175] text-[#1E7735];}.Purple-Outline{@apply bg-[#F6EAFD] border-[#CE8EF2] text-[#9130C0];}.Cyan-Outline{@apply bg-[#F1FAFE] border-[#3FC2F1] text-[#096E9B];}.Gray-Transparent{@apply bg-transparent border-[#BBBDC5] text-[#363C51];}.Primary-Transparent{@apply bg-transparent border-[#3F71FF] text-[#1434CB];}.Error-Transparent{@apply bg-transparent border-[#FA727A] text-[#CB1432];}.Warning-Transparent{@apply bg-transparent border-[#FBAE58] text-[#A04C02];}.Success-Transparent{@apply bg-transparent border-[#57D175] text-[#1E7735];}.Purple-Transparent{@apply bg-transparent border-[#CE8EF2] text-[#9130C0];}.Cyan-Transparent{@apply bg-transparent border-[#3FC2F1] text-[#096E9B];}\n"] }]
2491
2495
  }], propDecorators: { label: [{
2492
2496
  type: Input
2493
2497
  }], variant: [{
@@ -2748,6 +2752,259 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2748
2752
  type: Output
2749
2753
  }] } });
2750
2754
 
2755
+ class Textarea {
2756
+ ngControl;
2757
+ name;
2758
+ id;
2759
+ label = '';
2760
+ placeholder = '';
2761
+ rows = 4;
2762
+ hint = '';
2763
+ required = false;
2764
+ maxLength = null;
2765
+ hasError = false;
2766
+ errorMessage = '';
2767
+ trimWhiteSpaces = false;
2768
+ eventType = 'input';
2769
+ value = '';
2770
+ isDisabled = false;
2771
+ // --- ControlValueAccessor ---
2772
+ onChange = (_) => { };
2773
+ onTouched = () => { };
2774
+ constructor(ngControl) {
2775
+ this.ngControl = ngControl;
2776
+ if (this.ngControl) {
2777
+ this.ngControl.valueAccessor = this;
2778
+ }
2779
+ }
2780
+ // --- Computed ID ---
2781
+ get effectiveId() {
2782
+ const base = 'brickclay_textarea_';
2783
+ return this.id ? `${base}${this.id}` : `${base}${this.label?.replace(/\s+/g, '_').toLowerCase()}`;
2784
+ }
2785
+ // --- Expose FormControl state ---
2786
+ get control() {
2787
+ return this.ngControl?.control;
2788
+ }
2789
+ get touched() {
2790
+ return this.control?.touched;
2791
+ }
2792
+ get dirty() {
2793
+ return this.control?.dirty;
2794
+ }
2795
+ get errors() {
2796
+ return this.control?.errors;
2797
+ }
2798
+ get showError() {
2799
+ debugger;
2800
+ if (this.hasError)
2801
+ return true;
2802
+ if (!this.control)
2803
+ return false;
2804
+ return (this.required && (this.control.dirty || this.control.touched) &&
2805
+ this.control.invalid &&
2806
+ this.control.errors?.['required']);
2807
+ }
2808
+ // --- Event handler ---
2809
+ handleEvent(event) {
2810
+ const input = event.target;
2811
+ // Always update internal value for the counter
2812
+ this.value = input.value;
2813
+ // Trim spaces if needed
2814
+ if (this.trimWhiteSpaces && this.eventType === 'input') {
2815
+ setTimeout(() => {
2816
+ this.value = this.value.trim();
2817
+ input.value = this.value;
2818
+ if (this.eventType === 'input')
2819
+ this.onChange(this.value);
2820
+ }, 300); // small delay for input event
2821
+ }
2822
+ else if (this.trimWhiteSpaces) {
2823
+ this.value = this.value.trim();
2824
+ input.value = this.value;
2825
+ }
2826
+ // Update ngModel depending on event type
2827
+ if (this.eventType === 'input' && event.type === 'input')
2828
+ this.onChange(this.value);
2829
+ if (this.eventType === 'change' && event.type === 'change')
2830
+ this.onChange(this.value);
2831
+ if (this.eventType === 'blur' && event.type === 'blur')
2832
+ this.onChange(this.value);
2833
+ // Always mark as touched on blur/focus
2834
+ if (event.type === 'blur' || event.type === 'focus')
2835
+ this.onTouched();
2836
+ }
2837
+ writeValue(value) {
2838
+ this.value = value ?? '';
2839
+ }
2840
+ registerOnChange(fn) {
2841
+ this.onChange = fn;
2842
+ }
2843
+ registerOnTouched(fn) {
2844
+ this.onTouched = fn;
2845
+ }
2846
+ setDisabledState(isDisabled) {
2847
+ this.isDisabled = isDisabled;
2848
+ }
2849
+ 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 });
2850
+ 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 }] });
2851
+ }
2852
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: Textarea, decorators: [{
2853
+ type: Component,
2854
+ 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" }]
2855
+ }], ctorParameters: () => [{ type: i1$1.NgControl, decorators: [{
2856
+ type: Optional
2857
+ }, {
2858
+ type: Self
2859
+ }] }], propDecorators: { name: [{
2860
+ type: Input
2861
+ }], id: [{
2862
+ type: Input
2863
+ }], label: [{
2864
+ type: Input
2865
+ }], placeholder: [{
2866
+ type: Input
2867
+ }], rows: [{
2868
+ type: Input
2869
+ }], hint: [{
2870
+ type: Input
2871
+ }], required: [{
2872
+ type: Input
2873
+ }], maxLength: [{
2874
+ type: Input
2875
+ }], hasError: [{
2876
+ type: Input
2877
+ }], errorMessage: [{
2878
+ type: Input
2879
+ }], trimWhiteSpaces: [{
2880
+ type: Input
2881
+ }], eventType: [{
2882
+ type: Input
2883
+ }] } });
2884
+
2885
+ class Table {
2886
+ draggable = false;
2887
+ columns = [];
2888
+ result;
2889
+ actions = [];
2890
+ actionClick = new EventEmitter();
2891
+ sortChange = new EventEmitter();
2892
+ dragDropChange = new EventEmitter();
2893
+ sortColumn;
2894
+ sortDirection = 'asc';
2895
+ tableScrollContainer;
2896
+ get firstVisibleColumnIndex() {
2897
+ const index = this.columns.findIndex(col => col.visible !== false);
2898
+ return index >= 0 ? index : 0;
2899
+ }
2900
+ /* ---------- Sorting ---------- */
2901
+ sort(column, index) {
2902
+ if (!column.sortable || !column.field)
2903
+ return;
2904
+ // Toggle sort direction
2905
+ this.sortDirection =
2906
+ this.sortColumn === column.field ? (this.sortDirection === 'asc' ? 'desc' : 'asc') : 'asc';
2907
+ this.sortColumn = column.field;
2908
+ // Emit sort change separately
2909
+ this.sortChange.emit({
2910
+ columnIndex: index,
2911
+ column,
2912
+ direction: this.sortDirection,
2913
+ });
2914
+ }
2915
+ /* ---------- Visibility ---------- */
2916
+ isColumnVisible(column) {
2917
+ return column.visible !== false;
2918
+ }
2919
+ /* ---------- Cell Value ---------- */
2920
+ getCellValue(row, column) {
2921
+ if (column.formatter) {
2922
+ return column.formatter(row);
2923
+ }
2924
+ if (column.field) {
2925
+ return String(row[column.field] ?? '');
2926
+ }
2927
+ return '';
2928
+ }
2929
+ /* ---------- Actions ---------- */
2930
+ emitAction(action, row) {
2931
+ this.actionClick.emit({
2932
+ action: action.name,
2933
+ row,
2934
+ });
2935
+ }
2936
+ dropList(event) {
2937
+ if (!this.draggable || !this.result)
2938
+ return;
2939
+ moveItemInArray(this.result, event.previousIndex, event.currentIndex);
2940
+ // Update existing sortOrder on T
2941
+ this.result.forEach((item, index) => {
2942
+ item.sortOrder = index + 1;
2943
+ });
2944
+ // Emit reordered list
2945
+ this.dragDropChange.emit(this.result);
2946
+ }
2947
+ onDragMoved(event) {
2948
+ if (!this.tableScrollContainer)
2949
+ return;
2950
+ const container = this.tableScrollContainer.nativeElement;
2951
+ const rect = container.getBoundingClientRect();
2952
+ const pointerY = event.pointerPosition.y - rect.top;
2953
+ const threshold = 80;
2954
+ const maxSpeed = 25;
2955
+ if (pointerY < threshold) {
2956
+ const intensity = 1 - pointerY / threshold;
2957
+ container.scrollTop -= Math.min(maxSpeed, intensity * maxSpeed);
2958
+ }
2959
+ else if (pointerY > rect.height - threshold) {
2960
+ const intensity = 1 - (rect.height - pointerY) / threshold;
2961
+ container.scrollTop += Math.min(maxSpeed, intensity * maxSpeed);
2962
+ }
2963
+ }
2964
+ onDragStart(event) {
2965
+ const row = event.source.element.nativeElement;
2966
+ const cells = Array.from(row.querySelectorAll('td'));
2967
+ setTimeout(() => {
2968
+ const preview = document.querySelector('.cdk-drag-preview');
2969
+ if (!preview)
2970
+ return;
2971
+ const previewCells = preview.querySelectorAll('td');
2972
+ cells.forEach((cell, index) => {
2973
+ const width = cell.getBoundingClientRect().width + 'px';
2974
+ const previewCell = previewCells[index];
2975
+ if (previewCell) {
2976
+ previewCell.style.width = width;
2977
+ previewCell.style.minWidth = width;
2978
+ previewCell.style.maxWidth = width;
2979
+ }
2980
+ });
2981
+ });
2982
+ }
2983
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: Table, deps: [], target: i0.ɵɵFactoryTarget.Component });
2984
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: Table, isStandalone: true, selector: "brickclay-table", inputs: { draggable: "draggable", columns: "columns", result: "result", actions: "actions" }, outputs: { actionClick: "actionClick", sortChange: "sortChange", dragDropChange: "dragDropChange" }, viewQueries: [{ propertyName: "tableScrollContainer", first: true, predicate: ["tableScrollContainer"], descendants: true }], ngImport: i0, template: "<div\r\n #tableScrollContainer\r\n cdkScrollable\r\n class=\"h-[calc(100vh-260px)] overflow-y-auto\">\r\n\r\n <table class=\"min-w-full text-sm text-left text-gray-800 table-auto border-collapse\">\r\n\r\n <!-- ================= HEADER ================= -->\r\n <thead>\r\n <tr>\r\n @for (col of columns; track col.header;let i=$index;) {\r\n @if (isColumnVisible(col)) {\r\n <th\r\n class=\"table-header sticky top-0 cursor-pointer\"\r\n [class.action-sticky]=\"col.sticky\"\r\n [class.z-10]=\"col.sticky\"\r\n [ngClass]=\"col.headerClass\"\r\n [ngClass]=\"col.cellClass\"\r\n (click)=\"sort(col,i)\"\r\n >\r\n <span class=\"flex items-center gap-1\"\r\n [ngClass]=\"sortColumn === col.field\r\n ? (sortDirection === 'asc' ? 'asc' : 'desc')\r\n : ''\">\r\n {{ col.header }}\r\n @if (col.sortable) {\r\n <span class=\"sort-icon\"></span>\r\n }\r\n </span>\r\n </th>\r\n }\r\n }\r\n\r\n @if (actions.length) {\r\n <th class=\"table-header sticky top-0 action-sticky z-10 !bg-[#FBFBFC] w-20\">\r\n Action\r\n </th>\r\n }\r\n </tr>\r\n </thead>\r\n\r\n <!-- ================= BODY ================= -->\r\n <tbody\r\n cdkDropList\r\n [cdkDropListDisabled]=\"!draggable\"\r\n [cdkDropListData]=\"result || []\"\r\n (cdkDropListDropped)=\"dropList($event)\">\r\n\r\n @for (row of result; track row; let rowIndex = $index) {\r\n <tr\r\n cdkDrag\r\n cdkDragLockAxis=\"y\"\r\n [cdkDragDisabled]=\"!draggable\"\r\n (cdkDragStarted)=\"onDragStart($event)\"\r\n (cdkDragMoved)=\"onDragMoved($event)\"\r\n class=\"\" [ngClass]=\"{ 'cursor-move ': draggable }\">\r\n\r\n @for (col of columns; track col.header; let colIndex = $index) {\r\n @if (isColumnVisible(col)) {\r\n <td class=\"table-cell text-nowrap\" [ngClass]=\"col.cellClass\">\r\n @if (draggable && colIndex === firstVisibleColumnIndex) {\r\n <span cdkDragHandle class=\"mr-2 text-gray-400\" [ngClass]=\"{ 'cursor-move': draggable }\">\u2630</span>\r\n }\r\n {{ getCellValue(row, col) }}\r\n </td>\r\n }\r\n }\r\n\r\n @if (actions.length) {\r\n <td class=\"table-cell action-sticky text-center\">\r\n <div class=\"flex items-center justify-center gap-1.5\">\r\n @for (action of actions; track action.name) {\r\n @if (action.hasPermission) {\r\n <!-- appTooltip=\"{{ action.tooltip }}\"\r\n appTooltipPosition=\"top\" -->\r\n <button\r\n class=\"size-6 flex items-center justify-center rounded hover:bg-[#F8F8FA]\"\r\n (click)=\"emitAction(action, row)\"\r\n\r\n >\r\n <img\r\n [src]=\"action.icon\"\r\n width=\"14\"\r\n height=\"14\"\r\n alt=\"action-icon\"\r\n />\r\n </button>\r\n }\r\n }\r\n </div>\r\n </td>\r\n }\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n</div>\r\n", styles: [".cdk-drag-preview{display:table;width:100%;background:#fff;box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f}.cdk-drag-placeholder{opacity:.4;background-color:#f3f4f6}.cdk-drag-animating,.cdk-drop-list-dragging .cdk-drag{transition:transform .25s cubic-bezier(0,0,.2,1)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i2.ɵɵCdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }, { kind: "directive", type: i2.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i2.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i2.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: ScrollingModule }] });
2985
+ }
2986
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: Table, decorators: [{
2987
+ type: Component,
2988
+ args: [{ selector: 'brickclay-table', standalone: true, imports: [CommonModule, DragDropModule, ScrollingModule], template: "<div\r\n #tableScrollContainer\r\n cdkScrollable\r\n class=\"h-[calc(100vh-260px)] overflow-y-auto\">\r\n\r\n <table class=\"min-w-full text-sm text-left text-gray-800 table-auto border-collapse\">\r\n\r\n <!-- ================= HEADER ================= -->\r\n <thead>\r\n <tr>\r\n @for (col of columns; track col.header;let i=$index;) {\r\n @if (isColumnVisible(col)) {\r\n <th\r\n class=\"table-header sticky top-0 cursor-pointer\"\r\n [class.action-sticky]=\"col.sticky\"\r\n [class.z-10]=\"col.sticky\"\r\n [ngClass]=\"col.headerClass\"\r\n [ngClass]=\"col.cellClass\"\r\n (click)=\"sort(col,i)\"\r\n >\r\n <span class=\"flex items-center gap-1\"\r\n [ngClass]=\"sortColumn === col.field\r\n ? (sortDirection === 'asc' ? 'asc' : 'desc')\r\n : ''\">\r\n {{ col.header }}\r\n @if (col.sortable) {\r\n <span class=\"sort-icon\"></span>\r\n }\r\n </span>\r\n </th>\r\n }\r\n }\r\n\r\n @if (actions.length) {\r\n <th class=\"table-header sticky top-0 action-sticky z-10 !bg-[#FBFBFC] w-20\">\r\n Action\r\n </th>\r\n }\r\n </tr>\r\n </thead>\r\n\r\n <!-- ================= BODY ================= -->\r\n <tbody\r\n cdkDropList\r\n [cdkDropListDisabled]=\"!draggable\"\r\n [cdkDropListData]=\"result || []\"\r\n (cdkDropListDropped)=\"dropList($event)\">\r\n\r\n @for (row of result; track row; let rowIndex = $index) {\r\n <tr\r\n cdkDrag\r\n cdkDragLockAxis=\"y\"\r\n [cdkDragDisabled]=\"!draggable\"\r\n (cdkDragStarted)=\"onDragStart($event)\"\r\n (cdkDragMoved)=\"onDragMoved($event)\"\r\n class=\"\" [ngClass]=\"{ 'cursor-move ': draggable }\">\r\n\r\n @for (col of columns; track col.header; let colIndex = $index) {\r\n @if (isColumnVisible(col)) {\r\n <td class=\"table-cell text-nowrap\" [ngClass]=\"col.cellClass\">\r\n @if (draggable && colIndex === firstVisibleColumnIndex) {\r\n <span cdkDragHandle class=\"mr-2 text-gray-400\" [ngClass]=\"{ 'cursor-move': draggable }\">\u2630</span>\r\n }\r\n {{ getCellValue(row, col) }}\r\n </td>\r\n }\r\n }\r\n\r\n @if (actions.length) {\r\n <td class=\"table-cell action-sticky text-center\">\r\n <div class=\"flex items-center justify-center gap-1.5\">\r\n @for (action of actions; track action.name) {\r\n @if (action.hasPermission) {\r\n <!-- appTooltip=\"{{ action.tooltip }}\"\r\n appTooltipPosition=\"top\" -->\r\n <button\r\n class=\"size-6 flex items-center justify-center rounded hover:bg-[#F8F8FA]\"\r\n (click)=\"emitAction(action, row)\"\r\n\r\n >\r\n <img\r\n [src]=\"action.icon\"\r\n width=\"14\"\r\n height=\"14\"\r\n alt=\"action-icon\"\r\n />\r\n </button>\r\n }\r\n }\r\n </div>\r\n </td>\r\n }\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n</div>\r\n", styles: [".cdk-drag-preview{display:table;width:100%;background:#fff;box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f}.cdk-drag-placeholder{opacity:.4;background-color:#f3f4f6}.cdk-drag-animating,.cdk-drop-list-dragging .cdk-drag{transition:transform .25s cubic-bezier(0,0,.2,1)}\n"] }]
2989
+ }], propDecorators: { draggable: [{
2990
+ type: Input
2991
+ }], columns: [{
2992
+ type: Input
2993
+ }], result: [{
2994
+ type: Input
2995
+ }], actions: [{
2996
+ type: Input
2997
+ }], actionClick: [{
2998
+ type: Output
2999
+ }], sortChange: [{
3000
+ type: Output
3001
+ }], dragDropChange: [{
3002
+ type: Output
3003
+ }], tableScrollContainer: [{
3004
+ type: ViewChild,
3005
+ args: ['tableScrollContainer', { static: false }]
3006
+ }] } });
3007
+
2751
3008
  /*
2752
3009
  * Public API Surface of brickclay-lib
2753
3010
  */
@@ -2757,5 +3014,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2757
3014
  * Generated bundle index. Do not edit.
2758
3015
  */
2759
3016
 
2760
- export { BadgeComponent, BrickclayIcons, BrickclayLib, ButtonGroup, CalendarManagerService, CalendarModule, CheckboxComponent, CustomCalendarComponent, PillComponent, RadioComponent, ScheduledDatePickerComponent, Spinner, TimePickerComponent, ToggleComponent, UiButton, UiIconButton };
3017
+ export { BadgeComponent, BrickclayIcons, BrickclayLib, ButtonGroup, CalendarManagerService, CalendarModule, CheckboxComponent, CustomCalendarComponent, PillComponent, RadioComponent, ScheduledDatePickerComponent, Spinner, Table, Textarea, TimePickerComponent, ToggleComponent, UiButton, UiIconButton };
2761
3018
  //# sourceMappingURL=brickclay-org-ui.mjs.map