@haloduck/ui 2.0.9 → 2.0.11

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.
@@ -2784,6 +2784,237 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
2784
2784
  args: ['label']
2785
2785
  }] } });
2786
2786
 
2787
+ class TabsComponent {
2788
+ tabs = [];
2789
+ selectedIndex = 0;
2790
+ layout = 'vertical';
2791
+ labelWidth = '';
2792
+ selectedIndexChange = new EventEmitter();
2793
+ label;
2794
+ ngOnChanges(changes) {
2795
+ if (changes['tabs']) {
2796
+ this.ensureSelectedInRange();
2797
+ }
2798
+ if (changes['selectedIndex']) {
2799
+ this.ensureSelectedInRange();
2800
+ }
2801
+ }
2802
+ ngAfterViewInit() {
2803
+ // hide label if no content.
2804
+ if (this.label && this.label.nativeElement) {
2805
+ const hasContent = this.label.nativeElement.textContent?.trim();
2806
+ if (!hasContent) {
2807
+ this.label.nativeElement.style.display = 'none';
2808
+ }
2809
+ }
2810
+ }
2811
+ get current() {
2812
+ if (!this.tabs || this.tabs.length === 0)
2813
+ return null;
2814
+ const idx = Math.min(Math.max(this.selectedIndex, 0), this.tabs.length - 1);
2815
+ return this.tabs[idx];
2816
+ }
2817
+ select(index) {
2818
+ if (index < 0 || index >= this.tabs.length)
2819
+ return;
2820
+ this.selectedIndex = index;
2821
+ this.selectedIndexChange.emit(this.selectedIndex);
2822
+ }
2823
+ ensureSelectedInRange() {
2824
+ if (!this.tabs || this.tabs.length === 0) {
2825
+ this.selectedIndex = 0;
2826
+ return;
2827
+ }
2828
+ if (this.selectedIndex < 0)
2829
+ this.selectedIndex = 0;
2830
+ if (this.selectedIndex > this.tabs.length - 1)
2831
+ this.selectedIndex = this.tabs.length - 1;
2832
+ }
2833
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: TabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2834
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.4", type: TabsComponent, isStandalone: true, selector: "haloduck-tabs", inputs: { tabs: "tabs", selectedIndex: "selectedIndex", layout: "layout", labelWidth: "labelWidth" }, outputs: { selectedIndexChange: "selectedIndexChange" }, viewQueries: [{ propertyName: "label", first: true, predicate: ["label"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex gap-2 items-start w-full\" [ngClass]=\"{'flex-col': layout === 'vertical'}\">\n <label #label class=\"block text-sm/6 font-medium text-light-on-control dark:text-dark-on-control {{ labelWidth }}\" [ngClass]=\"{'w-full': layout === 'vertical'}\">\n <ng-content select=\"[slot=label]\"></ng-content>\n </label>\n\n <div class=\"flex-1 w-full\">\n <div class=\"flex items-center gap-1 border-b border-light-inactive dark:border-dark-inactive text-sm/6\">\n @for (tab of tabs; track tab; let i = $index) {\n <button type=\"button\"\n class=\"px-3 py-2 rounded-t-md\"\n [ngClass]=\"{\n 'text-light-on-control dark:text-dark-on-control': i !== selectedIndex,\n 'text-light-primary dark:text-dark-primary border-b-2 border-light-primary dark:border-dark-primary': i === selectedIndex\n }\"\n (click)=\"select(i)\">\n {{ tab.label }}\n </button>\n }\n </div>\n\n <div class=\"mt-3\">\n @if (current) {\n <ng-container *ngComponentOutlet=\"current!.component; inputs: current!.inputs || {}\"></ng-container>\n }\n </div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }] });
2835
+ }
2836
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: TabsComponent, decorators: [{
2837
+ type: Component,
2838
+ args: [{ selector: 'haloduck-tabs', imports: [CommonModule], template: "<div class=\"flex gap-2 items-start w-full\" [ngClass]=\"{'flex-col': layout === 'vertical'}\">\n <label #label class=\"block text-sm/6 font-medium text-light-on-control dark:text-dark-on-control {{ labelWidth }}\" [ngClass]=\"{'w-full': layout === 'vertical'}\">\n <ng-content select=\"[slot=label]\"></ng-content>\n </label>\n\n <div class=\"flex-1 w-full\">\n <div class=\"flex items-center gap-1 border-b border-light-inactive dark:border-dark-inactive text-sm/6\">\n @for (tab of tabs; track tab; let i = $index) {\n <button type=\"button\"\n class=\"px-3 py-2 rounded-t-md\"\n [ngClass]=\"{\n 'text-light-on-control dark:text-dark-on-control': i !== selectedIndex,\n 'text-light-primary dark:text-dark-primary border-b-2 border-light-primary dark:border-dark-primary': i === selectedIndex\n }\"\n (click)=\"select(i)\">\n {{ tab.label }}\n </button>\n }\n </div>\n\n <div class=\"mt-3\">\n @if (current) {\n <ng-container *ngComponentOutlet=\"current!.component; inputs: current!.inputs || {}\"></ng-container>\n }\n </div>\n </div>\n</div>\n" }]
2839
+ }], propDecorators: { tabs: [{
2840
+ type: Input
2841
+ }], selectedIndex: [{
2842
+ type: Input
2843
+ }], layout: [{
2844
+ type: Input
2845
+ }], labelWidth: [{
2846
+ type: Input
2847
+ }], selectedIndexChange: [{
2848
+ type: Output
2849
+ }], label: [{
2850
+ type: ViewChild,
2851
+ args: ['label']
2852
+ }] } });
2853
+
2854
+ class TagInputComponent {
2855
+ label;
2856
+ inputEl;
2857
+ placeholder = '';
2858
+ disabled = false;
2859
+ allowDuplicates = false;
2860
+ // Two-way binding support independent of CVA
2861
+ set value(tags) {
2862
+ if (Array.isArray(tags)) {
2863
+ this.tags = tags
2864
+ .map((t) => (t ?? '').trim())
2865
+ .filter((t) => t.length > 0);
2866
+ }
2867
+ else {
2868
+ this.tags = [];
2869
+ }
2870
+ }
2871
+ valueChange = new EventEmitter();
2872
+ tags = [];
2873
+ inputValue = '';
2874
+ onChange = () => { };
2875
+ onTouched = () => { };
2876
+ writeValue(value) {
2877
+ if (Array.isArray(value)) {
2878
+ this.tags = value
2879
+ .map((t) => (t ?? '').trim())
2880
+ .filter((t) => t.length > 0);
2881
+ }
2882
+ else {
2883
+ this.tags = [];
2884
+ }
2885
+ }
2886
+ registerOnChange(fn) {
2887
+ this.onChange = fn;
2888
+ }
2889
+ registerOnTouched(fn) {
2890
+ this.onTouched = fn;
2891
+ }
2892
+ setDisabledState(isDisabled) {
2893
+ this.disabled = isDisabled;
2894
+ }
2895
+ ngAfterViewInit() {
2896
+ // hide label if no projected content
2897
+ if (this.label && this.label.nativeElement) {
2898
+ const hasContent = this.label.nativeElement.textContent?.trim();
2899
+ if (!hasContent) {
2900
+ this.label.nativeElement.style.display = 'none';
2901
+ }
2902
+ }
2903
+ }
2904
+ focus() {
2905
+ this.inputEl?.nativeElement?.focus();
2906
+ }
2907
+ onInput(event) {
2908
+ const input = event.target;
2909
+ this.inputValue = input.value;
2910
+ this.onTouched();
2911
+ }
2912
+ onBlur() {
2913
+ if (this.disabled)
2914
+ return;
2915
+ this.inputValue = '';
2916
+ this.onTouched();
2917
+ }
2918
+ onKeydown(event) {
2919
+ if (this.disabled)
2920
+ return;
2921
+ // Commit on comma or Enter
2922
+ if (event.key === ',' || event.key === 'Enter') {
2923
+ event.preventDefault();
2924
+ this.commitCurrentInput();
2925
+ return;
2926
+ }
2927
+ // Backspace behavior
2928
+ if (event.key === 'Backspace') {
2929
+ if (this.inputValue.length > 0) {
2930
+ return; // default backspace in input
2931
+ }
2932
+ if (this.tags.length > 0) {
2933
+ event.preventDefault();
2934
+ this.removeTag(this.tags.length - 1);
2935
+ return;
2936
+ }
2937
+ }
2938
+ }
2939
+ removeTag(index) {
2940
+ if (this.disabled)
2941
+ return;
2942
+ if (index < 0 || index >= this.tags.length)
2943
+ return;
2944
+ this.tags = this.tags.filter((_, i) => i !== index);
2945
+ this.emitChanges();
2946
+ }
2947
+ commitCurrentInput() {
2948
+ const raw = this.inputValue.trim();
2949
+ if (!raw) {
2950
+ this.inputValue = '';
2951
+ return;
2952
+ }
2953
+ // If user pasted multiple comma-separated values, split and add all
2954
+ const parts = raw.split(',').map((p) => p.trim()).filter((p) => p.length > 0);
2955
+ for (const part of parts) {
2956
+ this.addTag(part);
2957
+ }
2958
+ this.inputValue = '';
2959
+ }
2960
+ addTag(tag) {
2961
+ if (!this.allowDuplicates) {
2962
+ const exists = this.tags.some((t) => t.toLowerCase() === tag.toLowerCase());
2963
+ if (exists) {
2964
+ return;
2965
+ }
2966
+ }
2967
+ this.tags = [...this.tags, tag];
2968
+ this.emitChanges();
2969
+ }
2970
+ emitChanges() {
2971
+ const cleaned = this.tags
2972
+ .map((t) => (t ?? '').trim())
2973
+ .filter((t) => t.length > 0);
2974
+ if (cleaned.length !== this.tags.length || cleaned.some((t, i) => t !== this.tags[i])) {
2975
+ this.tags = cleaned;
2976
+ }
2977
+ this.onChange(this.tags);
2978
+ this.valueChange.emit(this.tags);
2979
+ }
2980
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: TagInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2981
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.4", type: TagInputComponent, isStandalone: true, selector: "haloduck-tag-input", inputs: { placeholder: "placeholder", disabled: "disabled", allowDuplicates: "allowDuplicates", value: "value" }, outputs: { valueChange: "valueChange" }, providers: [
2982
+ {
2983
+ provide: NG_VALUE_ACCESSOR,
2984
+ useExisting: forwardRef(() => TagInputComponent),
2985
+ multi: true,
2986
+ },
2987
+ provideTranslocoScope('haloduck'),
2988
+ ], viewQueries: [{ propertyName: "label", first: true, predicate: ["label"], descendants: true }, { propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true }], ngImport: i0, template: "<div class=\"flex flex-col gap-2\">\n <label #label class=\"block text-sm/6 font-medium text-light-on-control dark:text-dark-on-control text-left\">\n <ng-content></ng-content>\n </label>\n\n <div class=\"block w-full rounded-md bg-light-control dark:bg-dark-control disabled:bg-light-control/60 dark:disabled:bg-dark-control/80 px-2 py-1.5 text-base text-light-on-control dark:text-dark-on-control disabled:cursor-not-allowed disabled:text-light-on-control/60 dark:disabled:text-dark-on-control/80 outline -outline-offset-1 outline-light-inactive dark:outline-dark-inactive placeholder:text-light-inactive dark:placeholder:text-dark-inactive focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-light-primary dark:focus-within:outline-dark-primary sm:text-sm/6\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <ng-container *ngFor=\"let tag of tags; let i = index\">\n <span class=\"inline-flex items-center gap-1 rounded-md bg-light-secondary dark:bg-dark-secondary text-light-on-secondary dark:text-dark-on-secondary px-2 py-0.5 text-xs\">\n {{ tag }}\n @if (!disabled) {\n <button type=\"button\" (click)=\"removeTag(i)\" class=\"text-light-on-secondary/80 hover:text-light-on-secondary dark:text-dark-on-secondary/80 dark:hover:text-dark-on-secondary hover:cursor-pointer\">\n \u00D7\n </button>\n }\n </span>\n </ng-container>\n\n <input #inputEl\n [disabled]=\"disabled\"\n [placeholder]=\"placeholder\"\n class=\"flex-1 min-w-[8rem] bg-transparent outline-none placeholder:text-light-inactive dark:placeholder:text-dark-inactive\"\n [value]=\"inputValue\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n />\n </div>\n </div>\n</div>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
2989
+ }
2990
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: TagInputComponent, decorators: [{
2991
+ type: Component,
2992
+ args: [{ selector: 'haloduck-tag-input', imports: [CommonModule], providers: [
2993
+ {
2994
+ provide: NG_VALUE_ACCESSOR,
2995
+ useExisting: forwardRef(() => TagInputComponent),
2996
+ multi: true,
2997
+ },
2998
+ provideTranslocoScope('haloduck'),
2999
+ ], template: "<div class=\"flex flex-col gap-2\">\n <label #label class=\"block text-sm/6 font-medium text-light-on-control dark:text-dark-on-control text-left\">\n <ng-content></ng-content>\n </label>\n\n <div class=\"block w-full rounded-md bg-light-control dark:bg-dark-control disabled:bg-light-control/60 dark:disabled:bg-dark-control/80 px-2 py-1.5 text-base text-light-on-control dark:text-dark-on-control disabled:cursor-not-allowed disabled:text-light-on-control/60 dark:disabled:text-dark-on-control/80 outline -outline-offset-1 outline-light-inactive dark:outline-dark-inactive placeholder:text-light-inactive dark:placeholder:text-dark-inactive focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-light-primary dark:focus-within:outline-dark-primary sm:text-sm/6\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <ng-container *ngFor=\"let tag of tags; let i = index\">\n <span class=\"inline-flex items-center gap-1 rounded-md bg-light-secondary dark:bg-dark-secondary text-light-on-secondary dark:text-dark-on-secondary px-2 py-0.5 text-xs\">\n {{ tag }}\n @if (!disabled) {\n <button type=\"button\" (click)=\"removeTag(i)\" class=\"text-light-on-secondary/80 hover:text-light-on-secondary dark:text-dark-on-secondary/80 dark:hover:text-dark-on-secondary hover:cursor-pointer\">\n \u00D7\n </button>\n }\n </span>\n </ng-container>\n\n <input #inputEl\n [disabled]=\"disabled\"\n [placeholder]=\"placeholder\"\n class=\"flex-1 min-w-[8rem] bg-transparent outline-none placeholder:text-light-inactive dark:placeholder:text-dark-inactive\"\n [value]=\"inputValue\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n />\n </div>\n </div>\n</div>\n", styles: [":host{display:block}\n"] }]
3000
+ }], propDecorators: { label: [{
3001
+ type: ViewChild,
3002
+ args: ['label']
3003
+ }], inputEl: [{
3004
+ type: ViewChild,
3005
+ args: ['inputEl']
3006
+ }], placeholder: [{
3007
+ type: Input
3008
+ }], disabled: [{
3009
+ type: Input
3010
+ }], allowDuplicates: [{
3011
+ type: Input
3012
+ }], value: [{
3013
+ type: Input
3014
+ }], valueChange: [{
3015
+ type: Output
3016
+ }] } });
3017
+
2787
3018
  class BreadcrumbService {
2788
3019
  router;
2789
3020
  coreService = inject(CoreService);
@@ -2896,5 +3127,5 @@ const provideHaloduckTransloco = () => provideTranslocoScope({
2896
3127
  * Generated bundle index. Do not edit.
2897
3128
  */
2898
3129
 
2899
- export { AuthenticateComponent, BreadcrumbComponent, ButtonComponent, CalendarComponent, ConfirmDialogService, CopyButtonComponent, DatePickerComponent, DateRangeComponent, DialogService, DrawCanvasComponent, ERROR_NOT_ACCEPTABLE_FILE_TYPE, ERROR_OVER_COUNT, ERROR_OVER_SIZE, ERROR_UPLOAD, FileUploaderComponent, FlipComponent, ImageUploaderComponent, ImageViewerComponent, InputComponent, LanguageSelectorComponent, MapToAddressComponent, NotificationComponent, NotificationService, PictureNameComponent, SelectComponent, SelectDropdownComponent, SideMenuComponent, SideMenuItemComponent, StlViewerComponent, TableComponent, ToggleComponent, dateToString, provideHaloduckTransloco };
3130
+ export { AuthenticateComponent, BreadcrumbComponent, ButtonComponent, CalendarComponent, ConfirmDialogService, CopyButtonComponent, DatePickerComponent, DateRangeComponent, DialogService, DrawCanvasComponent, ERROR_NOT_ACCEPTABLE_FILE_TYPE, ERROR_OVER_COUNT, ERROR_OVER_SIZE, ERROR_UPLOAD, FileUploaderComponent, FlipComponent, ImageUploaderComponent, ImageViewerComponent, InputComponent, LanguageSelectorComponent, MapToAddressComponent, NotificationComponent, NotificationService, PictureNameComponent, SelectComponent, SelectDropdownComponent, SideMenuComponent, SideMenuItemComponent, StlViewerComponent, TableComponent, TabsComponent, TagInputComponent, ToggleComponent, dateToString, provideHaloduckTransloco };
2900
3131
  //# sourceMappingURL=haloduck-ui.mjs.map