@eduboxpro/studio 0.1.7 → 0.1.9

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.
@@ -1,10 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, signal, effect, Injectable, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, input, computed, ChangeDetectionStrategy, Component, output, forwardRef, viewChild, model } from '@angular/core';
2
+ import { InjectionToken, inject, signal, effect, Injectable, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, input, computed, ChangeDetectionStrategy, Component, output, ElementRef, forwardRef, viewChild, model, Renderer2, DestroyRef, HostListener, Directive } from '@angular/core';
3
3
  import * as i1$1 from '@angular/common';
4
4
  import { DOCUMENT, CommonModule } from '@angular/common';
5
5
  import * as i1 from 'lucide-angular';
6
- import { icons, LucideAngularModule, LucideIconProvider, LUCIDE_ICONS, Moon, Sun, ShoppingCart, Loader2, Loader, RotateCw, RefreshCw, Printer, Save, Image, Folder, FileText, File, HelpCircle, XCircle, CheckCircle, Info, AlertTriangle, AlertCircle, LogOut, LogIn, Unlock, Lock, EyeOff, Eye, Clock, Calendar, Bell, MoreHorizontal, MoreVertical, Menu, Home, Share2, Copy, Link, ExternalLink, Filter, Search, X, Check, Minus, Plus, Edit, Trash2, User, Settings, Star, Heart, Phone, Mail, Upload, Download, ChevronRight, ChevronLeft, ChevronUp, ChevronDown, ArrowLeft, ArrowRight } from 'lucide-angular';
7
- import { NG_VALUE_ACCESSOR } from '@angular/forms';
6
+ import { icons, LucideAngularModule, LucideIconProvider, LUCIDE_ICONS, Circle, Apple, Globe, Laptop, MessageSquare, List, Grid3x3, Strikethrough, Underline, Italic, Bold, AlignJustify, AlignRight, AlignCenter, AlignLeft, Moon, Sun, ShoppingCart, Loader2, Loader, RotateCw, RefreshCw, Printer, Save, Image, Folder, FileText, File, HelpCircle, XCircle, CheckCircle, Info, AlertTriangle, AlertCircle, LogOut, LogIn, Unlock, Lock, EyeOff, Eye, Clock, Calendar, Bell, MoreHorizontal, MoreVertical, Menu, Home, Share2, Copy, Link, ExternalLink, Filter, Search, X, Check, Minus, Plus, Edit, Trash2, User, Settings, Star, Heart, Phone, Mail, Upload, Download, ChevronRight, ChevronLeft, ChevronUp, ChevronDown, ArrowLeft, ArrowRight } from 'lucide-angular';
7
+ import { NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
8
8
 
9
9
  /**
10
10
  * Injection token for Studio configuration
@@ -443,7 +443,22 @@ function provideStudioIcons() {
443
443
  Loader2,
444
444
  ShoppingCart,
445
445
  Sun,
446
- Moon
446
+ Moon,
447
+ AlignLeft,
448
+ AlignCenter,
449
+ AlignRight,
450
+ AlignJustify,
451
+ Bold,
452
+ Italic,
453
+ Underline,
454
+ Strikethrough,
455
+ Grid3x3,
456
+ List,
457
+ MessageSquare,
458
+ Laptop,
459
+ Globe,
460
+ Apple,
461
+ Circle
447
462
  })
448
463
  }
449
464
  ]);
@@ -869,6 +884,67 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
869
884
  * Button component
870
885
  */
871
886
 
887
+ class ButtonGroupComponent {
888
+ elementRef = inject(ElementRef);
889
+ configService = inject(StudioConfigService);
890
+ groupDefaults = computed(() => this.configService.config().components?.buttonGroup, ...(ngDevMode ? [{ debugName: "groupDefaults" }] : []));
891
+ orientationInput = input(undefined, ...(ngDevMode ? [{ debugName: "orientationInput", alias: 'orientation' }] : [{ alias: 'orientation' }]));
892
+ sizeInput = input(undefined, ...(ngDevMode ? [{ debugName: "sizeInput", alias: 'size' }] : [{ alias: 'size' }]));
893
+ variantInput = input(undefined, ...(ngDevMode ? [{ debugName: "variantInput", alias: 'variant' }] : [{ alias: 'variant' }]));
894
+ colorInput = input(undefined, ...(ngDevMode ? [{ debugName: "colorInput", alias: 'color' }] : [{ alias: 'color' }]));
895
+ radiusInput = input(undefined, ...(ngDevMode ? [{ debugName: "radiusInput", alias: 'radius' }] : [{ alias: 'radius' }]));
896
+ orientation = withConfigDefault(this.orientationInput, computed(() => this.groupDefaults()?.orientation), 'horizontal');
897
+ size = withConfigDefault(this.sizeInput, computed(() => this.groupDefaults()?.size), undefined);
898
+ variant = withConfigDefault(this.variantInput, computed(() => this.groupDefaults()?.variant), undefined);
899
+ color = withConfigDefault(this.colorInput, computed(() => this.groupDefaults()?.color), undefined);
900
+ radius = withConfigDefault(this.radiusInput, computed(() => this.groupDefaults()?.radius), undefined);
901
+ attached = input(false, ...(ngDevMode ? [{ debugName: "attached" }] : []));
902
+ fullWidth = input(false, ...(ngDevMode ? [{ debugName: "fullWidth" }] : []));
903
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
904
+ spacing = input('sm', ...(ngDevMode ? [{ debugName: "spacing" }] : []));
905
+ align = input('start', ...(ngDevMode ? [{ debugName: "align" }] : []));
906
+ ariaLabel = input(...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : []));
907
+ role = input('group', ...(ngDevMode ? [{ debugName: "role" }] : []));
908
+ hostClasses = computed(() => classNames('studio-button-group', `studio-button-group--${this.orientation()}`, `studio-button-group--spacing-${this.spacing()}`, `studio-button-group--align-${this.align()}`, this.attached() && 'studio-button-group--attached', this.fullWidth() && 'studio-button-group--full', this.disabled() && 'studio-button-group--disabled'), ...(ngDevMode ? [{ debugName: "hostClasses" }] : []));
909
+ constructor() {
910
+ effect(() => {
911
+ const isAttached = this.attached();
912
+ const hostElement = this.elementRef.nativeElement;
913
+ const buttonElements = hostElement.querySelectorAll('studio-button');
914
+ buttonElements.forEach((buttonEl, index) => {
915
+ const isFirst = index === 0;
916
+ const isLast = index === buttonElements.length - 1;
917
+ const isMiddle = !isFirst && !isLast;
918
+ if (isAttached) {
919
+ buttonEl.classList.add('studio-button-group__item--attached');
920
+ if (isFirst)
921
+ buttonEl.classList.add('studio-button-group__item--first');
922
+ if (isLast)
923
+ buttonEl.classList.add('studio-button-group__item--last');
924
+ if (isMiddle)
925
+ buttonEl.classList.add('studio-button-group__item--middle');
926
+ }
927
+ else {
928
+ buttonEl.classList.remove('studio-button-group__item--attached');
929
+ buttonEl.classList.remove('studio-button-group__item--first');
930
+ buttonEl.classList.remove('studio-button-group__item--last');
931
+ buttonEl.classList.remove('studio-button-group__item--middle');
932
+ }
933
+ });
934
+ });
935
+ }
936
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: ButtonGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
937
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.12", type: ButtonGroupComponent, isStandalone: true, selector: "studio-button-group", inputs: { orientationInput: { classPropertyName: "orientationInput", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, sizeInput: { classPropertyName: "sizeInput", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, variantInput: { classPropertyName: "variantInput", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, colorInput: { classPropertyName: "colorInput", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, radiusInput: { classPropertyName: "radiusInput", publicName: "radius", isSignal: true, isRequired: false, transformFunction: null }, attached: { classPropertyName: "attached", publicName: "attached", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, spacing: { classPropertyName: "spacing", publicName: "spacing", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()", "attr.role": "role()", "attr.aria-label": "ariaLabel()" } }, ngImport: i0, template: `<ng-content select="studio-button" />`, isInline: true, styles: [":host{display:inline-flex;align-items:center;position:relative}:host(.studio-button-group--horizontal){flex-direction:row}:host(.studio-button-group--vertical){flex-direction:column}:host(.studio-button-group--spacing-none){gap:0}:host(.studio-button-group--spacing-xs){gap:var(--studio-spacing-xs)}:host(.studio-button-group--spacing-sm){gap:var(--studio-spacing-sm)}:host(.studio-button-group--spacing-md){gap:var(--studio-spacing-md)}:host(.studio-button-group--spacing-lg){gap:var(--studio-spacing-lg)}:host(.studio-button-group--align-start){justify-content:flex-start}:host(.studio-button-group--align-center){justify-content:center}:host(.studio-button-group--align-end){justify-content:flex-end}:host(.studio-button-group--align-stretch){align-items:stretch}:host(.studio-button-group--align-stretch) ::ng-deep studio-button{flex:1}:host(.studio-button-group--full){width:100%}:host(.studio-button-group--full) ::ng-deep studio-button{flex:1}:host(.studio-button-group--attached){gap:0}:host(.studio-button-group--attached) ::ng-deep studio-button.studio-button-group__item--attached{margin:0;position:relative}:host(.studio-button-group--attached) ::ng-deep studio-button.studio-button-group__item--middle{border-radius:0}:host(.studio-button-group--attached) ::ng-deep studio-button.studio-button-group__item--first:not(.studio-button-group__item--last){border-top-right-radius:0;border-bottom-right-radius:0}:host(.studio-button-group--attached) ::ng-deep studio-button.studio-button-group__item--last:not(.studio-button-group__item--first){border-top-left-radius:0;border-bottom-left-radius:0}:host(.studio-button-group--attached).studio-button-group--horizontal ::ng-deep studio-button.studio-button-group__item--attached:not(:last-child){border-right:1px solid rgba(0,0,0,.1)}:host(.studio-button-group--attached).studio-button-group--horizontal ::ng-deep studio-button.studio-button-group__item--attached.studio-button--outline:not(:last-child){margin-right:-1px}:host(.studio-button-group--vertical){align-items:stretch}:host(.studio-button-group--vertical) ::ng-deep studio-button{width:100%;justify-content:flex-start}:host(.studio-button-group--vertical.studio-button-group--attached) ::ng-deep studio-button.studio-button-group__item--first:not(.studio-button-group__item--last){border-top-right-radius:var(--studio-radius-sm);border-bottom-right-radius:0;border-top-left-radius:var(--studio-radius-sm);border-bottom-left-radius:0}:host(.studio-button-group--vertical.studio-button-group--attached) ::ng-deep studio-button.studio-button-group__item--last:not(.studio-button-group__item--first){border-top-left-radius:0;border-bottom-left-radius:var(--studio-radius-sm);border-top-right-radius:0;border-bottom-right-radius:var(--studio-radius-sm)}:host(.studio-button-group--vertical.studio-button-group--attached) ::ng-deep studio-button.studio-button-group__item--attached:not(:last-child){border-right:none;border-bottom:1px solid rgba(255,255,255,.2)}:host(.studio-button-group--vertical.studio-button-group--attached) ::ng-deep studio-button.studio-button-group__item--middle{border-radius:0}:host(.studio-button-group--vertical.studio-button-group--full) ::ng-deep studio-button{width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
938
+ }
939
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: ButtonGroupComponent, decorators: [{
940
+ type: Component,
941
+ args: [{ selector: 'studio-button-group', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, host: {
942
+ '[class]': 'hostClasses()',
943
+ '[attr.role]': 'role()',
944
+ '[attr.aria-label]': 'ariaLabel()'
945
+ }, template: `<ng-content select="studio-button" />`, styles: [":host{display:inline-flex;align-items:center;position:relative}:host(.studio-button-group--horizontal){flex-direction:row}:host(.studio-button-group--vertical){flex-direction:column}:host(.studio-button-group--spacing-none){gap:0}:host(.studio-button-group--spacing-xs){gap:var(--studio-spacing-xs)}:host(.studio-button-group--spacing-sm){gap:var(--studio-spacing-sm)}:host(.studio-button-group--spacing-md){gap:var(--studio-spacing-md)}:host(.studio-button-group--spacing-lg){gap:var(--studio-spacing-lg)}:host(.studio-button-group--align-start){justify-content:flex-start}:host(.studio-button-group--align-center){justify-content:center}:host(.studio-button-group--align-end){justify-content:flex-end}:host(.studio-button-group--align-stretch){align-items:stretch}:host(.studio-button-group--align-stretch) ::ng-deep studio-button{flex:1}:host(.studio-button-group--full){width:100%}:host(.studio-button-group--full) ::ng-deep studio-button{flex:1}:host(.studio-button-group--attached){gap:0}:host(.studio-button-group--attached) ::ng-deep studio-button.studio-button-group__item--attached{margin:0;position:relative}:host(.studio-button-group--attached) ::ng-deep studio-button.studio-button-group__item--middle{border-radius:0}:host(.studio-button-group--attached) ::ng-deep studio-button.studio-button-group__item--first:not(.studio-button-group__item--last){border-top-right-radius:0;border-bottom-right-radius:0}:host(.studio-button-group--attached) ::ng-deep studio-button.studio-button-group__item--last:not(.studio-button-group__item--first){border-top-left-radius:0;border-bottom-left-radius:0}:host(.studio-button-group--attached).studio-button-group--horizontal ::ng-deep studio-button.studio-button-group__item--attached:not(:last-child){border-right:1px solid rgba(0,0,0,.1)}:host(.studio-button-group--attached).studio-button-group--horizontal ::ng-deep studio-button.studio-button-group__item--attached.studio-button--outline:not(:last-child){margin-right:-1px}:host(.studio-button-group--vertical){align-items:stretch}:host(.studio-button-group--vertical) ::ng-deep studio-button{width:100%;justify-content:flex-start}:host(.studio-button-group--vertical.studio-button-group--attached) ::ng-deep studio-button.studio-button-group__item--first:not(.studio-button-group__item--last){border-top-right-radius:var(--studio-radius-sm);border-bottom-right-radius:0;border-top-left-radius:var(--studio-radius-sm);border-bottom-left-radius:0}:host(.studio-button-group--vertical.studio-button-group--attached) ::ng-deep studio-button.studio-button-group__item--last:not(.studio-button-group__item--first){border-top-left-radius:0;border-bottom-left-radius:var(--studio-radius-sm);border-top-right-radius:0;border-bottom-right-radius:var(--studio-radius-sm)}:host(.studio-button-group--vertical.studio-button-group--attached) ::ng-deep studio-button.studio-button-group__item--attached:not(:last-child){border-right:none;border-bottom:1px solid rgba(255,255,255,.2)}:host(.studio-button-group--vertical.studio-button-group--attached) ::ng-deep studio-button.studio-button-group__item--middle{border-radius:0}:host(.studio-button-group--vertical.studio-button-group--full) ::ng-deep studio-button{width:100%}\n"] }]
946
+ }], ctorParameters: () => [], propDecorators: { orientationInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], sizeInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], variantInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], colorInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], radiusInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "radius", required: false }] }], attached: [{ type: i0.Input, args: [{ isSignal: true, alias: "attached", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], spacing: [{ type: i0.Input, args: [{ isSignal: true, alias: "spacing", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }] } });
947
+
872
948
  /**
873
949
  * Checkbox component for selecting boolean values
874
950
  *
@@ -1259,6 +1335,281 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
1259
1335
  }, template: "@if (label() && !floatingLabel()) {\n <label [for]=\"id() || generatedId\" class=\"studio-input__label\">\n {{ label() }}\n @if (required()) {\n <span class=\"studio-input__required\">*</span>\n }\n </label>\n}\n\n<div class=\"studio-input__container\">\n @if (prefixIcon()) {\n <span class=\"studio-input__prefix\">\n <studio-icon [name]=\"prefixIcon()!\" [size]=\"16\" />\n </span>\n }\n\n @if (label() && floatingLabel()) {\n <label [for]=\"id() || generatedId\" class=\"studio-input__label--floating\">\n {{ label() }}\n @if (required()) {\n <span>*</span>\n }\n </label>\n }\n\n <input\n #inputElement\n class=\"studio-input__field\"\n [type]=\"computedType()\"\n [value]=\"value()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [placeholder]=\"computedPlaceholder()\"\n [required]=\"required()\"\n [attr.maxlength]=\"maxLength()\"\n [attr.minlength]=\"minLength()\"\n [min]=\"min()\"\n [max]=\"max()\"\n [step]=\"step()\"\n [pattern]=\"pattern()\"\n [attr.inputmode]=\"inputmode()\"\n [attr.autocomplete]=\"autocomplete()\"\n [name]=\"name()\"\n [id]=\"id() || generatedId\"\n [attr.aria-label]=\"ariaLabel() || (!label() ? placeholder() : null)\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"ariaDescribedByValue()\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n (input)=\"handleInput($event)\"\n (focus)=\"handleFocus($event)\"\n (blur)=\"handleBlur($event)\"\n (keydown.enter)=\"handleEnter($event)\"\n />\n\n @if (loading()) {\n <span class=\"studio-input__loading\">\n <studio-icon name=\"loader-2\" [size]=\"16\" class=\"studio-icon--spin\" />\n </span>\n }\n\n @if (clearable() && value() && !disabled() && !loading()) {\n <button\n type=\"button\"\n class=\"studio-input__clear\"\n (click)=\"handleClear()\"\n [attr.aria-label]=\"'Clear input'\"\n tabindex=\"-1\"\n >\n <studio-icon name=\"x\" [size]=\"14\" />\n </button>\n }\n\n @if (type() === 'password' && showPasswordToggle() && !loading()) {\n <button\n type=\"button\"\n class=\"studio-input__toggle-password\"\n (click)=\"togglePasswordVisibility()\"\n [attr.aria-label]=\"isPasswordVisible() ? 'Hide password' : 'Show password'\"\n tabindex=\"-1\"\n >\n <studio-icon [name]=\"isPasswordVisible() ? 'eye-off' : 'eye'\" [size]=\"16\" />\n </button>\n }\n\n @if (suffixIcon() && !loading() && !clearable() && !(type() === 'password' && showPasswordToggle())) {\n <span class=\"studio-input__suffix\">\n <studio-icon [name]=\"suffixIcon()!\" [size]=\"16\" />\n </span>\n }\n </div>\n\n@if (hint() && !error()) {\n <span class=\"studio-input__hint\" [id]=\"hintId\">\n {{ hint() }}\n </span>\n}\n\n@if (error() && errorMessage()) {\n <span class=\"studio-input__error\" [id]=\"errorId\" role=\"alert\">\n <studio-icon name=\"alert-circle\" [size]=\"14\" />\n {{ errorMessage() }}\n </span>\n}\n", styles: [":host{display:flex;flex-direction:column;gap:.375rem;font-family:var(--studio-font-family)}:host(.studio-input--full-width){width:100%}.studio-input__label{display:block;font-size:.875rem;font-weight:var(--studio-font-weight-medium);color:var(--studio-text-primary);margin-bottom:.25rem}.studio-input__label .studio-input__required{color:var(--studio-error);margin-left:.125rem}.studio-input__container{position:relative;display:flex;align-items:center;transition:all var(--studio-transition-fast)}.studio-input__field{flex:1;width:100%;font-family:inherit;font-size:1rem;color:var(--studio-text-primary);background:transparent;border:none;outline:none;transition:all var(--studio-transition-fast)}.studio-input__field::placeholder{color:var(--studio-text-tertiary)}.studio-input__field:disabled{cursor:not-allowed;opacity:.6}.studio-input__field:read-only{cursor:default}.studio-input__prefix,.studio-input__suffix{display:flex;align-items:center;justify-content:center;color:var(--studio-text-secondary);flex-shrink:0}.studio-input__clear,.studio-input__toggle-password{display:flex;align-items:center;justify-content:center;background:none;border:none;padding:.25rem;color:var(--studio-text-secondary);cursor:pointer;border-radius:var(--studio-radius-sm);transition:all var(--studio-transition-fast);flex-shrink:0}.studio-input__clear:hover,.studio-input__toggle-password:hover{color:var(--studio-text-primary);background:var(--studio-bg-secondary)}.studio-input__clear:active,.studio-input__toggle-password:active{transform:scale(.95)}.studio-input__loading{display:flex;align-items:center;color:var(--studio-primary);flex-shrink:0}.studio-input__loading .studio-icon--spin{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.studio-input__hint{font-size:.75rem;color:var(--studio-text-secondary);line-height:1.4}.studio-input__error{display:flex;align-items:center;gap:.25rem;font-size:.75rem;color:var(--studio-error);line-height:1.4}:host(.studio-input--outline) .studio-input__container{border:1px solid var(--studio-border-primary);background:var(--studio-bg-primary);border-radius:var(--studio-radius-md)}:host(.studio-input--outline) .studio-input__container:hover:not(:has(.studio-input__field:disabled)){border-color:var(--studio-primary)}:host(.studio-input--outline.studio-input--focused) .studio-input__container{border-color:var(--studio-primary);box-shadow:0 0 0 3px var(--studio-primary-bg)}:host(.studio-input--outline.studio-input--error) .studio-input__container{border-color:var(--studio-error)}:host(.studio-input--outline.studio-input--error) .studio-input__container:hover{border-color:var(--studio-error)}:host(.studio-input--outline.studio-input--error.studio-input--focused) .studio-input__container{box-shadow:0 0 0 3px #ef44441a}:host(.studio-input--outline.studio-input--disabled) .studio-input__container{background:var(--studio-bg-secondary);border-color:var(--studio-border-secondary)}:host(.studio-input--filled) .studio-input__container{background:var(--studio-bg-secondary);border:none;border-bottom:2px solid var(--studio-border-primary);border-radius:var(--studio-radius-md) var(--studio-radius-md) 0 0}:host(.studio-input--filled) .studio-input__container:hover:not(:has(.studio-input__field:disabled)){background:var(--studio-bg-tertiary)}:host(.studio-input--filled.studio-input--focused) .studio-input__container{border-bottom-color:var(--studio-primary);background:var(--studio-bg-tertiary)}:host(.studio-input--filled.studio-input--error) .studio-input__container{border-bottom-color:var(--studio-error)}:host(.studio-input--filled.studio-input--disabled) .studio-input__container{background:var(--studio-bg-secondary);opacity:.6}:host(.studio-input--underline) .studio-input__container{background:transparent;border:none;border-bottom:1px solid var(--studio-border-primary);border-radius:0}:host(.studio-input--underline) .studio-input__container:hover:not(:has(.studio-input__field:disabled)){border-bottom-color:var(--studio-primary)}:host(.studio-input--underline.studio-input--focused) .studio-input__container{border-bottom:2px solid var(--studio-primary)}:host(.studio-input--underline.studio-input--error) .studio-input__container{border-bottom-color:var(--studio-error)}:host(.studio-input--underline.studio-input--disabled) .studio-input__container{border-bottom-style:dashed;opacity:.6}:host(.studio-input--sm) .studio-input__field{height:2rem;font-size:.875rem;padding:0 .75rem}:host(.studio-input--sm) .studio-input__prefix{padding-left:.75rem}:host(.studio-input--sm) .studio-input__suffix,:host(.studio-input--sm) .studio-input__clear,:host(.studio-input--sm) .studio-input__toggle-password,:host(.studio-input--sm) .studio-input__loading{padding-right:.5rem}:host(.studio-input--md) .studio-input__field{height:2.5rem;font-size:1rem;padding:0 1rem}:host(.studio-input--md) .studio-input__prefix{padding-left:1rem}:host(.studio-input--md) .studio-input__suffix,:host(.studio-input--md) .studio-input__clear,:host(.studio-input--md) .studio-input__toggle-password,:host(.studio-input--md) .studio-input__loading{padding-right:.75rem}:host(.studio-input--lg) .studio-input__field{height:3rem;font-size:1.125rem;padding:0 1.25rem}:host(.studio-input--lg) .studio-input__prefix{padding-left:1.25rem}:host(.studio-input--lg) .studio-input__suffix,:host(.studio-input--lg) .studio-input__clear,:host(.studio-input--lg) .studio-input__toggle-password,:host(.studio-input--lg) .studio-input__loading{padding-right:1rem}.studio-input__label--floating{position:absolute;top:50%;left:1rem;transform:translateY(-50%);font-size:1rem;color:var(--studio-text-tertiary);pointer-events:none;transition:all var(--studio-transition-fast);background:var(--studio-bg-primary);padding:0 .25rem}:host(.studio-input--filled) .studio-input__label--floating{background:transparent}:host(.studio-input--focused) .studio-input__label--floating,:host(.studio-input--has-value) .studio-input__label--floating{top:0;font-size:.75rem;color:var(--studio-primary)}:host(.studio-input--error) .studio-input__label--floating{color:var(--studio-error)}:host(.studio-input--radius-none) .studio-input__container{border-radius:0}:host(.studio-input--radius-sm) .studio-input__container{border-radius:var(--studio-radius-sm)}:host(.studio-input--radius-md) .studio-input__container{border-radius:var(--studio-radius-md)}:host(.studio-input--radius-lg) .studio-input__container{border-radius:var(--studio-radius-lg)}:host(.studio-input--radius-full) .studio-input__container{border-radius:9999px}:host(.studio-input--filled.studio-input--radius-none) .studio-input__container{border-radius:0}:host(.studio-input--filled.studio-input--radius-sm) .studio-input__container{border-radius:var(--studio-radius-sm) var(--studio-radius-sm) 0 0}:host(.studio-input--filled.studio-input--radius-md) .studio-input__container{border-radius:var(--studio-radius-md) var(--studio-radius-md) 0 0}:host(.studio-input--filled.studio-input--radius-lg) .studio-input__container{border-radius:var(--studio-radius-lg) var(--studio-radius-lg) 0 0}:host(.studio-input--filled.studio-input--radius-full) .studio-input__container{border-radius:9999px 9999px 0 0}\n"] }]
1260
1336
  }], ctorParameters: () => [], propDecorators: { inputEl: [{ type: i0.ViewChild, args: ['inputElement', { isSignal: true }] }], variantInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], sizeInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], radiusInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "radius", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], inputmode: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputmode", required: false }] }], autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocomplete", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], minLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minLength", required: false }] }], 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 }] }], pattern: [{ type: i0.Input, args: [{ isSignal: true, alias: "pattern", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], floatingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "floatingLabel", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], prefixIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefixIcon", required: false }] }], suffixIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffixIcon", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], showPasswordToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPasswordToggle", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], autoFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoFocus", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], focused: [{ type: i0.Output, args: ["focused"] }], blurred: [{ type: i0.Output, args: ["blurred"] }], cleared: [{ type: i0.Output, args: ["cleared"] }], entered: [{ type: i0.Output, args: ["entered"] }] } });
1261
1337
 
1338
+ class SelectComponent {
1339
+ configService = inject(StudioConfigService);
1340
+ selectDefaults = computed(() => this.configService.config().components?.select, ...(ngDevMode ? [{ debugName: "selectDefaults" }] : []));
1341
+ triggerEl = viewChild('triggerButton', ...(ngDevMode ? [{ debugName: "triggerEl" }] : []));
1342
+ dropdownEl = viewChild('dropdownPanel', ...(ngDevMode ? [{ debugName: "dropdownEl" }] : []));
1343
+ variantInput = input(undefined, ...(ngDevMode ? [{ debugName: "variantInput", alias: 'variant' }] : [{ alias: 'variant' }]));
1344
+ sizeInput = input(undefined, ...(ngDevMode ? [{ debugName: "sizeInput", alias: 'size' }] : [{ alias: 'size' }]));
1345
+ radiusInput = input(undefined, ...(ngDevMode ? [{ debugName: "radiusInput", alias: 'radius' }] : [{ alias: 'radius' }]));
1346
+ variant = withConfigDefault(this.variantInput, computed(() => this.selectDefaults()?.variant), 'outline');
1347
+ size = withConfigDefault(this.sizeInput, computed(() => this.selectDefaults()?.size), 'md');
1348
+ radius = withConfigDefault(this.radiusInput, computed(() => this.selectDefaults()?.radius), 'md');
1349
+ options = input.required(...(ngDevMode ? [{ debugName: "options" }] : []));
1350
+ placeholder = input('Select option...', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
1351
+ multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : []));
1352
+ searchable = input(false, ...(ngDevMode ? [{ debugName: "searchable" }] : []));
1353
+ clearable = input(true, ...(ngDevMode ? [{ debugName: "clearable" }] : []));
1354
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
1355
+ loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
1356
+ label = input(undefined, ...(ngDevMode ? [{ debugName: "label" }] : []));
1357
+ floatingLabel = input(false, ...(ngDevMode ? [{ debugName: "floatingLabel" }] : []));
1358
+ hint = input(undefined, ...(ngDevMode ? [{ debugName: "hint" }] : []));
1359
+ error = input(false, ...(ngDevMode ? [{ debugName: "error" }] : []));
1360
+ errorMessage = input(undefined, ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
1361
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
1362
+ prefixIcon = input(undefined, ...(ngDevMode ? [{ debugName: "prefixIcon" }] : []));
1363
+ suffixIcon = input('chevron-down', ...(ngDevMode ? [{ debugName: "suffixIcon" }] : []));
1364
+ fullWidth = input(false, ...(ngDevMode ? [{ debugName: "fullWidth" }] : []));
1365
+ maxHeight = input('300px', ...(ngDevMode ? [{ debugName: "maxHeight" }] : []));
1366
+ position = input('bottom', ...(ngDevMode ? [{ debugName: "position" }] : []));
1367
+ searchPlaceholder = input('Search...', ...(ngDevMode ? [{ debugName: "searchPlaceholder" }] : []));
1368
+ valueChange = output();
1369
+ searchChange = output();
1370
+ opened = output();
1371
+ closed = output();
1372
+ optionSelected = output();
1373
+ isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
1374
+ searchQuery = signal('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : []));
1375
+ selectedValue = signal(null, ...(ngDevMode ? [{ debugName: "selectedValue" }] : []));
1376
+ highlightedIndex = signal(0, ...(ngDevMode ? [{ debugName: "highlightedIndex" }] : []));
1377
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : []));
1378
+ isTouched = signal(false, ...(ngDevMode ? [{ debugName: "isTouched" }] : []));
1379
+ generatedId = `studio-select-${Math.random().toString(36).substr(2, 9)}`;
1380
+ onChange = () => { };
1381
+ onTouched = () => { };
1382
+ constructor() {
1383
+ effect(() => {
1384
+ if (this.isOpen()) {
1385
+ this.setupClickOutside();
1386
+ }
1387
+ else {
1388
+ this.cleanupClickOutside();
1389
+ }
1390
+ });
1391
+ }
1392
+ flattenedOptions = computed(() => {
1393
+ const opts = this.options();
1394
+ if (!opts || opts.length === 0)
1395
+ return [];
1396
+ if (this.isOptionGroup(opts[0])) {
1397
+ return opts.flatMap(g => g.options);
1398
+ }
1399
+ return opts;
1400
+ }, ...(ngDevMode ? [{ debugName: "flattenedOptions" }] : []));
1401
+ filteredOptions = computed(() => {
1402
+ const query = this.searchQuery().toLowerCase().trim();
1403
+ const opts = this.flattenedOptions();
1404
+ if (!query)
1405
+ return opts;
1406
+ return opts.filter(opt => opt.label.toLowerCase().includes(query) ||
1407
+ opt.description?.toLowerCase().includes(query));
1408
+ }, ...(ngDevMode ? [{ debugName: "filteredOptions" }] : []));
1409
+ selectedOptions = computed(() => {
1410
+ const value = this.selectedValue();
1411
+ const opts = this.flattenedOptions();
1412
+ if (this.multiple()) {
1413
+ const values = value || [];
1414
+ return opts.filter(opt => values.includes(opt.value));
1415
+ }
1416
+ return opts.find(opt => this.compareValues(opt.value, value)) || null;
1417
+ }, ...(ngDevMode ? [{ debugName: "selectedOptions" }] : []));
1418
+ displayValue = computed(() => {
1419
+ const selected = this.selectedOptions();
1420
+ if (this.multiple() && Array.isArray(selected)) {
1421
+ return selected.map(opt => opt.label).join(', ');
1422
+ }
1423
+ if (selected && !Array.isArray(selected)) {
1424
+ return selected.label;
1425
+ }
1426
+ return '';
1427
+ }, ...(ngDevMode ? [{ debugName: "displayValue" }] : []));
1428
+ hasValue = computed(() => {
1429
+ const val = this.selectedValue();
1430
+ return this.multiple()
1431
+ ? Array.isArray(val) && val.length > 0
1432
+ : val !== null && val !== undefined;
1433
+ }, ...(ngDevMode ? [{ debugName: "hasValue" }] : []));
1434
+ hostClasses = computed(() => classNames('studio-select', `studio-select--${this.variant()}`, `studio-select--${this.size()}`, `studio-select--radius-${this.radius()}`, this.isOpen() && 'studio-select--open', this.disabled() && 'studio-select--disabled', this.loading() && 'studio-select--loading', this.error() && 'studio-select--error', this.isFocused() && 'studio-select--focused', this.hasValue() && 'studio-select--has-value', this.fullWidth() && 'studio-select--full-width'), ...(ngDevMode ? [{ debugName: "hostClasses" }] : []));
1435
+ toggle() {
1436
+ if (!this.disabled() && !this.loading()) {
1437
+ this.isOpen.update(v => !v);
1438
+ if (this.isOpen()) {
1439
+ this.opened.emit();
1440
+ setTimeout(() => this.focusSearchInput(), 0);
1441
+ }
1442
+ else {
1443
+ this.closed.emit();
1444
+ this.searchQuery.set('');
1445
+ }
1446
+ }
1447
+ }
1448
+ close() {
1449
+ if (this.isOpen()) {
1450
+ this.isOpen.set(false);
1451
+ this.closed.emit();
1452
+ this.searchQuery.set('');
1453
+ this.isTouched.set(true);
1454
+ this.onTouched();
1455
+ }
1456
+ }
1457
+ selectOption(option) {
1458
+ if (option.disabled)
1459
+ return;
1460
+ if (this.multiple()) {
1461
+ const current = (this.selectedValue() || []);
1462
+ const isSelected = current.includes(option.value);
1463
+ if (isSelected) {
1464
+ this.selectedValue.set(current.filter(v => v !== option.value));
1465
+ }
1466
+ else {
1467
+ this.selectedValue.set([...current, option.value]);
1468
+ }
1469
+ }
1470
+ else {
1471
+ this.selectedValue.set(option.value);
1472
+ this.close();
1473
+ }
1474
+ this.onChange(this.selectedValue());
1475
+ this.valueChange.emit(this.selectedValue());
1476
+ this.optionSelected.emit(option);
1477
+ }
1478
+ clear(event) {
1479
+ event.stopPropagation();
1480
+ this.selectedValue.set(this.multiple() ? [] : null);
1481
+ this.onChange(this.selectedValue());
1482
+ this.valueChange.emit(this.selectedValue());
1483
+ }
1484
+ isSelected(option) {
1485
+ if (this.multiple()) {
1486
+ const values = this.selectedValue() || [];
1487
+ return values.includes(option.value);
1488
+ }
1489
+ return this.compareValues(option.value, this.selectedValue());
1490
+ }
1491
+ handleSearch(event) {
1492
+ const query = event.target.value;
1493
+ this.searchQuery.set(query);
1494
+ this.searchChange.emit(query);
1495
+ }
1496
+ handleKeydown(event) {
1497
+ switch (event.key) {
1498
+ case 'ArrowDown':
1499
+ event.preventDefault();
1500
+ if (!this.isOpen()) {
1501
+ this.toggle();
1502
+ }
1503
+ else {
1504
+ this.highlightNext();
1505
+ }
1506
+ break;
1507
+ case 'ArrowUp':
1508
+ event.preventDefault();
1509
+ if (this.isOpen()) {
1510
+ this.highlightPrevious();
1511
+ }
1512
+ break;
1513
+ case 'Enter':
1514
+ case ' ':
1515
+ event.preventDefault();
1516
+ if (this.isOpen()) {
1517
+ this.selectHighlighted();
1518
+ }
1519
+ else {
1520
+ this.toggle();
1521
+ }
1522
+ break;
1523
+ case 'Escape':
1524
+ if (this.isOpen()) {
1525
+ event.preventDefault();
1526
+ this.close();
1527
+ }
1528
+ break;
1529
+ }
1530
+ }
1531
+ highlightNext() {
1532
+ const opts = this.filteredOptions();
1533
+ const current = this.highlightedIndex();
1534
+ this.highlightedIndex.set(Math.min(current + 1, opts.length - 1));
1535
+ }
1536
+ highlightPrevious() {
1537
+ const current = this.highlightedIndex();
1538
+ this.highlightedIndex.set(Math.max(current - 1, 0));
1539
+ }
1540
+ selectHighlighted() {
1541
+ const opts = this.filteredOptions();
1542
+ const index = this.highlightedIndex();
1543
+ if (opts[index]) {
1544
+ this.selectOption(opts[index]);
1545
+ }
1546
+ }
1547
+ compareValues(a, b) {
1548
+ return a === b;
1549
+ }
1550
+ isOptionGroup(item) {
1551
+ return item && 'options' in item && Array.isArray(item.options);
1552
+ }
1553
+ focusSearchInput() {
1554
+ if (this.searchable()) {
1555
+ const searchInput = this.dropdownEl()?.nativeElement.querySelector('input');
1556
+ searchInput?.focus();
1557
+ }
1558
+ }
1559
+ clickOutsideListener;
1560
+ setupClickOutside() {
1561
+ this.clickOutsideListener = (event) => {
1562
+ const target = event.target;
1563
+ const trigger = this.triggerEl()?.nativeElement;
1564
+ const dropdown = this.dropdownEl()?.nativeElement;
1565
+ if (trigger && !trigger.contains(target) && dropdown && !dropdown.contains(target)) {
1566
+ this.close();
1567
+ }
1568
+ };
1569
+ setTimeout(() => {
1570
+ document.addEventListener('click', this.clickOutsideListener);
1571
+ }, 0);
1572
+ }
1573
+ cleanupClickOutside() {
1574
+ if (this.clickOutsideListener) {
1575
+ document.removeEventListener('click', this.clickOutsideListener);
1576
+ this.clickOutsideListener = undefined;
1577
+ }
1578
+ }
1579
+ writeValue(value) {
1580
+ this.selectedValue.set(value);
1581
+ }
1582
+ registerOnChange(fn) {
1583
+ this.onChange = fn;
1584
+ }
1585
+ registerOnTouched(fn) {
1586
+ this.onTouched = fn;
1587
+ }
1588
+ setDisabledState(isDisabled) {
1589
+ }
1590
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: SelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1591
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: SelectComponent, isStandalone: true, selector: "studio-select", inputs: { variantInput: { classPropertyName: "variantInput", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, sizeInput: { classPropertyName: "sizeInput", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, radiusInput: { classPropertyName: "radiusInput", publicName: "radius", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, floatingLabel: { classPropertyName: "floatingLabel", publicName: "floatingLabel", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, prefixIcon: { classPropertyName: "prefixIcon", publicName: "prefixIcon", isSignal: true, isRequired: false, transformFunction: null }, suffixIcon: { classPropertyName: "suffixIcon", publicName: "suffixIcon", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", searchChange: "searchChange", opened: "opened", closed: "closed", optionSelected: "optionSelected" }, host: { properties: { "class": "hostClasses()", "attr.data-open": "isOpen()" } }, providers: [
1592
+ {
1593
+ provide: NG_VALUE_ACCESSOR,
1594
+ useExisting: forwardRef(() => SelectComponent),
1595
+ multi: true
1596
+ }
1597
+ ], viewQueries: [{ propertyName: "triggerEl", first: true, predicate: ["triggerButton"], descendants: true, isSignal: true }, { propertyName: "dropdownEl", first: true, predicate: ["dropdownPanel"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (label() && !floatingLabel()) {\n <label [for]=\"generatedId\" class=\"studio-select__label\">\n {{ label() }}\n @if (required()) {\n <span class=\"studio-select__required\">*</span>\n }\n </label>\n}\n\n<div class=\"studio-select__control\">\n @if (prefixIcon()) {\n <span class=\"studio-select__prefix\">\n <studio-icon [name]=\"prefixIcon()!\" [size]=\"16\" />\n </span>\n }\n\n @if (label() && floatingLabel()) {\n <label [for]=\"generatedId\" class=\"studio-select__label--floating\">\n {{ label() }}\n @if (required()) {\n <span class=\"studio-select__required\">*</span>\n }\n </label>\n }\n\n <button\n #triggerButton\n type=\"button\"\n [id]=\"generatedId\"\n class=\"studio-select__trigger\"\n [disabled]=\"disabled() || loading()\"\n (click)=\"toggle()\"\n (keydown)=\"handleKeydown($event)\"\n (focus)=\"isFocused.set(true)\"\n (blur)=\"isFocused.set(false)\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-haspopup]=\"'listbox'\"\n >\n @if (hasValue()) {\n <span class=\"studio-select__value\">{{ displayValue() }}</span>\n } @else {\n <span class=\"studio-select__placeholder\">{{ placeholder() }}</span>\n }\n </button>\n\n @if (clearable() && hasValue() && !disabled() && !loading()) {\n <button\n type=\"button\"\n class=\"studio-select__clear\"\n (click)=\"clear($event)\"\n aria-label=\"Clear selection\"\n tabindex=\"-1\"\n >\n <studio-icon name=\"x\" [size]=\"14\" />\n </button>\n }\n\n @if (loading()) {\n <span class=\"studio-select__loading\">\n <studio-icon name=\"loader-2\" [size]=\"16\" class=\"studio-icon--spin\" />\n </span>\n } @else {\n <span class=\"studio-select__suffix\">\n <studio-icon\n [name]=\"suffixIcon()\"\n [size]=\"16\"\n />\n </span>\n }\n</div>\n\n@if (isOpen()) {\n <div\n #dropdownPanel\n class=\"studio-select__dropdown\"\n [attr.data-position]=\"position()\"\n [style.max-height]=\"maxHeight()\"\n >\n @if (searchable()) {\n <div class=\"studio-select__search\">\n <input\n type=\"text\"\n [placeholder]=\"searchPlaceholder()\"\n [value]=\"searchQuery()\"\n (input)=\"handleSearch($event)\"\n />\n </div>\n }\n\n <div class=\"studio-select__options\" role=\"listbox\">\n @if (filteredOptions().length === 0) {\n <div class=\"studio-select__empty\">\n No options found\n </div>\n }\n\n @for (option of filteredOptions(); track option.value; let idx = $index) {\n <div\n class=\"studio-select__option\"\n [class.studio-select__option--selected]=\"isSelected(option)\"\n [class.studio-select__option--disabled]=\"option.disabled\"\n [class.studio-select__option--highlighted]=\"highlightedIndex() === idx\"\n (click)=\"selectOption(option)\"\n role=\"option\"\n [attr.aria-selected]=\"isSelected(option)\"\n >\n @if (multiple()) {\n <input\n type=\"checkbox\"\n class=\"studio-select__checkbox\"\n [checked]=\"isSelected(option)\"\n [disabled]=\"option.disabled\"\n tabindex=\"-1\"\n />\n }\n\n @if (option.icon) {\n <studio-icon [name]=\"option.icon\" [size]=\"18\" class=\"studio-select__option-icon\" />\n }\n\n <div class=\"studio-select__option-content\">\n <div class=\"studio-select__option-label\">\n {{ option.label }}\n </div>\n @if (option.description) {\n <div class=\"studio-select__option-description\">\n {{ option.description }}\n </div>\n }\n </div>\n\n @if (!multiple() && isSelected(option)) {\n <studio-icon name=\"check\" [size]=\"16\" class=\"studio-select__check-icon\" />\n }\n </div>\n }\n </div>\n </div>\n}\n\n@if (hint() && !error()) {\n <div class=\"studio-select__hint\">{{ hint() }}</div>\n}\n\n@if (error() && errorMessage()) {\n <div class=\"studio-select__error\">{{ errorMessage() }}</div>\n}\n", styles: [":host{display:flex;flex-direction:column;gap:.375rem;font-family:var(--studio-font-family);position:relative}:host(.studio-select--full-width){width:100%}.studio-select__label{display:block;font-size:.875rem;font-weight:var(--studio-font-weight-medium);color:var(--studio-text-primary);margin-bottom:.25rem}.studio-select__label .studio-select__required{color:var(--studio-error);margin-left:.125rem}.studio-select__control{position:relative;display:flex;align-items:center;transition:all var(--studio-transition-fast)}.studio-select__prefix,.studio-select__suffix{display:flex;align-items:center;justify-content:center;color:var(--studio-text-secondary);flex-shrink:0}.studio-select__suffix{transition:var(--studio-transition-fast)}:host(.studio-select--open) .studio-select__suffix{transform:rotate(180deg)}.studio-select__trigger{flex:1;width:100%;display:flex;align-items:center;background:transparent;border:none;padding:0;font-family:inherit;text-align:left;color:var(--studio-text-primary);cursor:pointer;outline:none;transition:var(--studio-transition-fast)}.studio-select__trigger:disabled{cursor:not-allowed;opacity:.6}.studio-select__value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--studio-text-primary)}.studio-select__placeholder{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--studio-text-tertiary)}.studio-select__clear,.studio-select__loading{display:flex;align-items:center;justify-content:center;flex-shrink:0}.studio-select__clear{background:none;border:none;padding:.25rem;color:var(--studio-text-secondary);cursor:pointer;border-radius:var(--studio-radius-sm);transition:all var(--studio-transition-fast)}.studio-select__clear:hover{color:var(--studio-text-primary);background:var(--studio-bg-secondary)}.studio-select__clear:active{transform:scale(.95)}.studio-select__loading{color:var(--studio-primary)}.studio-select__loading .studio-icon--spin{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.studio-select__dropdown{position:absolute;top:100%;left:0;right:0;z-index:var(--studio-z-dropdown, 1000);background:var(--studio-bg-primary);border:1px solid var(--studio-border-primary);border-radius:var(--studio-radius-md);box-shadow:var(--studio-shadow-lg);margin-top:.25rem;overflow:hidden;display:flex;flex-direction:column}.studio-select__search{padding:.5rem;border-bottom:1px solid var(--studio-border-primary);flex-shrink:0}.studio-select__search input{width:100%;padding:.5rem;border:1px solid var(--studio-border-primary);border-radius:var(--studio-radius-sm);font-family:inherit;font-size:.875rem;color:var(--studio-text-primary);background:var(--studio-bg-primary);outline:none;transition:var(--studio-transition-fast)}.studio-select__search input:focus{border-color:var(--studio-primary);box-shadow:0 0 0 3px var(--studio-primary-bg)}.studio-select__search input::placeholder{color:var(--studio-text-tertiary)}.studio-select__options{overflow-y:auto;flex:1}.studio-select__option{display:flex;align-items:center;gap:.75rem;padding:.625rem .75rem;cursor:pointer;transition:var(--studio-transition-fast);color:var(--studio-text-primary)}.studio-select__option:hover{background:var(--studio-bg-secondary)}.studio-select__option--selected{background:var(--studio-primary-bg);color:var(--studio-primary)}.studio-select__option--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.studio-select__option input[type=checkbox]{margin:0;cursor:pointer}.studio-select__option-icon{flex-shrink:0;color:var(--studio-text-secondary)}.studio-select__option-content{flex:1;min-width:0}.studio-select__option-label{font-size:.875rem;color:inherit}.studio-select__option-description{font-size:.75rem;color:var(--studio-text-secondary);margin-top:.125rem}.studio-select__empty{padding:1rem;text-align:center;color:var(--studio-text-tertiary);font-size:.875rem}.studio-select__hint{font-size:.75rem;color:var(--studio-text-secondary);line-height:1.4}.studio-select__error{display:flex;align-items:center;gap:.25rem;font-size:.75rem;color:var(--studio-error);line-height:1.4}.studio-select__label--floating{position:absolute;top:50%;left:1rem;transform:translateY(-50%);font-size:1rem;color:var(--studio-text-tertiary);pointer-events:none;transition:all var(--studio-transition-fast);background:var(--studio-bg-primary);padding:0 .25rem;z-index:1}:host(.studio-select--focused) .studio-select__label--floating,:host(.studio-select--has-value) .studio-select__label--floating{top:0;font-size:.75rem;color:var(--studio-primary)}:host(.studio-select--error) .studio-select__label--floating{color:var(--studio-error)}:host(.studio-select--outline) .studio-select__control{border:1px solid var(--studio-border-primary);background:var(--studio-bg-primary);border-radius:var(--studio-radius-md)}:host(.studio-select--outline) .studio-select__control:hover:not(:has(.studio-select__trigger:disabled)){border-color:var(--studio-primary)}:host(.studio-select--outline.studio-select--focused) .studio-select__control{border-color:var(--studio-primary);box-shadow:0 0 0 3px var(--studio-primary-bg)}:host(.studio-select--outline.studio-select--error) .studio-select__control{border-color:var(--studio-error)}:host(.studio-select--outline.studio-select--error) .studio-select__control:hover{border-color:var(--studio-error)}:host(.studio-select--outline.studio-select--error.studio-select--focused) .studio-select__control{box-shadow:0 0 0 3px #ef44441a}:host(.studio-select--outline.studio-select--disabled) .studio-select__control{background:var(--studio-bg-secondary);border-color:var(--studio-border-secondary)}:host(.studio-select--filled) .studio-select__control{background:var(--studio-bg-secondary);border:none;border-bottom:2px solid var(--studio-border-primary);border-radius:var(--studio-radius-md) var(--studio-radius-md) 0 0}:host(.studio-select--filled) .studio-select__control:hover:not(:has(.studio-select__trigger:disabled)){background:var(--studio-bg-tertiary)}:host(.studio-select--filled.studio-select--focused) .studio-select__control{border-bottom-color:var(--studio-primary);background:var(--studio-bg-tertiary)}:host(.studio-select--filled.studio-select--error) .studio-select__control{border-bottom-color:var(--studio-error)}:host(.studio-select--filled.studio-select--disabled) .studio-select__control{background:var(--studio-bg-secondary);opacity:.6}:host(.studio-select--underline) .studio-select__control{background:transparent;border:none;border-bottom:1px solid var(--studio-border-primary);border-radius:0}:host(.studio-select--underline) .studio-select__control:hover:not(:has(.studio-select__trigger:disabled)){border-bottom-color:var(--studio-primary)}:host(.studio-select--underline.studio-select--focused) .studio-select__control{border-bottom:2px solid var(--studio-primary)}:host(.studio-select--underline.studio-select--error) .studio-select__control{border-bottom-color:var(--studio-error)}:host(.studio-select--underline.studio-select--disabled) .studio-select__control{border-bottom-style:dashed;opacity:.6}:host(.studio-select--sm) .studio-select__trigger{height:2rem;font-size:.875rem;padding:0 .75rem}:host(.studio-select--sm) .studio-select__prefix{padding-left:.75rem}:host(.studio-select--sm) .studio-select__suffix,:host(.studio-select--sm) .studio-select__clear,:host(.studio-select--sm) .studio-select__loading{padding-right:.5rem}:host(.studio-select--md) .studio-select__trigger{height:2.5rem;font-size:1rem;padding:0 1rem}:host(.studio-select--md) .studio-select__prefix{padding-left:1rem}:host(.studio-select--md) .studio-select__suffix,:host(.studio-select--md) .studio-select__clear,:host(.studio-select--md) .studio-select__loading{padding-right:.75rem}:host(.studio-select--lg) .studio-select__trigger{height:3rem;font-size:1.125rem;padding:0 1.25rem}:host(.studio-select--lg) .studio-select__prefix{padding-left:1.25rem}:host(.studio-select--lg) .studio-select__suffix,:host(.studio-select--lg) .studio-select__clear,:host(.studio-select--lg) .studio-select__loading{padding-right:1rem}:host(.studio-select--radius-none) .studio-select__control{border-radius:0}:host(.studio-select--radius-sm) .studio-select__control{border-radius:var(--studio-radius-sm)}:host(.studio-select--radius-md) .studio-select__control{border-radius:var(--studio-radius-md)}:host(.studio-select--radius-lg) .studio-select__control{border-radius:var(--studio-radius-lg)}:host(.studio-select--radius-full) .studio-select__control{border-radius:9999px}:host(.studio-select--filled.studio-select--radius-none) .studio-select__control{border-radius:0}:host(.studio-select--filled.studio-select--radius-sm) .studio-select__control{border-radius:var(--studio-radius-sm) var(--studio-radius-sm) 0 0}:host(.studio-select--filled.studio-select--radius-md) .studio-select__control{border-radius:var(--studio-radius-md) var(--studio-radius-md) 0 0}:host(.studio-select--filled.studio-select--radius-lg) .studio-select__control{border-radius:var(--studio-radius-lg) var(--studio-radius-lg) 0 0}:host(.studio-select--filled.studio-select--radius-full) .studio-select__control{border-radius:9999px 9999px 0 0}\n"], dependencies: [{ kind: "component", type: IconComponent, selector: "studio-icon", inputs: ["name", "size", "color", "strokeWidth", "absoluteStrokeWidth", "showFallback", "fallbackIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1598
+ }
1599
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: SelectComponent, decorators: [{
1600
+ type: Component,
1601
+ args: [{ selector: 'studio-select', standalone: true, imports: [IconComponent], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
1602
+ {
1603
+ provide: NG_VALUE_ACCESSOR,
1604
+ useExisting: forwardRef(() => SelectComponent),
1605
+ multi: true
1606
+ }
1607
+ ], host: {
1608
+ '[class]': 'hostClasses()',
1609
+ '[attr.data-open]': 'isOpen()',
1610
+ }, template: "@if (label() && !floatingLabel()) {\n <label [for]=\"generatedId\" class=\"studio-select__label\">\n {{ label() }}\n @if (required()) {\n <span class=\"studio-select__required\">*</span>\n }\n </label>\n}\n\n<div class=\"studio-select__control\">\n @if (prefixIcon()) {\n <span class=\"studio-select__prefix\">\n <studio-icon [name]=\"prefixIcon()!\" [size]=\"16\" />\n </span>\n }\n\n @if (label() && floatingLabel()) {\n <label [for]=\"generatedId\" class=\"studio-select__label--floating\">\n {{ label() }}\n @if (required()) {\n <span class=\"studio-select__required\">*</span>\n }\n </label>\n }\n\n <button\n #triggerButton\n type=\"button\"\n [id]=\"generatedId\"\n class=\"studio-select__trigger\"\n [disabled]=\"disabled() || loading()\"\n (click)=\"toggle()\"\n (keydown)=\"handleKeydown($event)\"\n (focus)=\"isFocused.set(true)\"\n (blur)=\"isFocused.set(false)\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-haspopup]=\"'listbox'\"\n >\n @if (hasValue()) {\n <span class=\"studio-select__value\">{{ displayValue() }}</span>\n } @else {\n <span class=\"studio-select__placeholder\">{{ placeholder() }}</span>\n }\n </button>\n\n @if (clearable() && hasValue() && !disabled() && !loading()) {\n <button\n type=\"button\"\n class=\"studio-select__clear\"\n (click)=\"clear($event)\"\n aria-label=\"Clear selection\"\n tabindex=\"-1\"\n >\n <studio-icon name=\"x\" [size]=\"14\" />\n </button>\n }\n\n @if (loading()) {\n <span class=\"studio-select__loading\">\n <studio-icon name=\"loader-2\" [size]=\"16\" class=\"studio-icon--spin\" />\n </span>\n } @else {\n <span class=\"studio-select__suffix\">\n <studio-icon\n [name]=\"suffixIcon()\"\n [size]=\"16\"\n />\n </span>\n }\n</div>\n\n@if (isOpen()) {\n <div\n #dropdownPanel\n class=\"studio-select__dropdown\"\n [attr.data-position]=\"position()\"\n [style.max-height]=\"maxHeight()\"\n >\n @if (searchable()) {\n <div class=\"studio-select__search\">\n <input\n type=\"text\"\n [placeholder]=\"searchPlaceholder()\"\n [value]=\"searchQuery()\"\n (input)=\"handleSearch($event)\"\n />\n </div>\n }\n\n <div class=\"studio-select__options\" role=\"listbox\">\n @if (filteredOptions().length === 0) {\n <div class=\"studio-select__empty\">\n No options found\n </div>\n }\n\n @for (option of filteredOptions(); track option.value; let idx = $index) {\n <div\n class=\"studio-select__option\"\n [class.studio-select__option--selected]=\"isSelected(option)\"\n [class.studio-select__option--disabled]=\"option.disabled\"\n [class.studio-select__option--highlighted]=\"highlightedIndex() === idx\"\n (click)=\"selectOption(option)\"\n role=\"option\"\n [attr.aria-selected]=\"isSelected(option)\"\n >\n @if (multiple()) {\n <input\n type=\"checkbox\"\n class=\"studio-select__checkbox\"\n [checked]=\"isSelected(option)\"\n [disabled]=\"option.disabled\"\n tabindex=\"-1\"\n />\n }\n\n @if (option.icon) {\n <studio-icon [name]=\"option.icon\" [size]=\"18\" class=\"studio-select__option-icon\" />\n }\n\n <div class=\"studio-select__option-content\">\n <div class=\"studio-select__option-label\">\n {{ option.label }}\n </div>\n @if (option.description) {\n <div class=\"studio-select__option-description\">\n {{ option.description }}\n </div>\n }\n </div>\n\n @if (!multiple() && isSelected(option)) {\n <studio-icon name=\"check\" [size]=\"16\" class=\"studio-select__check-icon\" />\n }\n </div>\n }\n </div>\n </div>\n}\n\n@if (hint() && !error()) {\n <div class=\"studio-select__hint\">{{ hint() }}</div>\n}\n\n@if (error() && errorMessage()) {\n <div class=\"studio-select__error\">{{ errorMessage() }}</div>\n}\n", styles: [":host{display:flex;flex-direction:column;gap:.375rem;font-family:var(--studio-font-family);position:relative}:host(.studio-select--full-width){width:100%}.studio-select__label{display:block;font-size:.875rem;font-weight:var(--studio-font-weight-medium);color:var(--studio-text-primary);margin-bottom:.25rem}.studio-select__label .studio-select__required{color:var(--studio-error);margin-left:.125rem}.studio-select__control{position:relative;display:flex;align-items:center;transition:all var(--studio-transition-fast)}.studio-select__prefix,.studio-select__suffix{display:flex;align-items:center;justify-content:center;color:var(--studio-text-secondary);flex-shrink:0}.studio-select__suffix{transition:var(--studio-transition-fast)}:host(.studio-select--open) .studio-select__suffix{transform:rotate(180deg)}.studio-select__trigger{flex:1;width:100%;display:flex;align-items:center;background:transparent;border:none;padding:0;font-family:inherit;text-align:left;color:var(--studio-text-primary);cursor:pointer;outline:none;transition:var(--studio-transition-fast)}.studio-select__trigger:disabled{cursor:not-allowed;opacity:.6}.studio-select__value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--studio-text-primary)}.studio-select__placeholder{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--studio-text-tertiary)}.studio-select__clear,.studio-select__loading{display:flex;align-items:center;justify-content:center;flex-shrink:0}.studio-select__clear{background:none;border:none;padding:.25rem;color:var(--studio-text-secondary);cursor:pointer;border-radius:var(--studio-radius-sm);transition:all var(--studio-transition-fast)}.studio-select__clear:hover{color:var(--studio-text-primary);background:var(--studio-bg-secondary)}.studio-select__clear:active{transform:scale(.95)}.studio-select__loading{color:var(--studio-primary)}.studio-select__loading .studio-icon--spin{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.studio-select__dropdown{position:absolute;top:100%;left:0;right:0;z-index:var(--studio-z-dropdown, 1000);background:var(--studio-bg-primary);border:1px solid var(--studio-border-primary);border-radius:var(--studio-radius-md);box-shadow:var(--studio-shadow-lg);margin-top:.25rem;overflow:hidden;display:flex;flex-direction:column}.studio-select__search{padding:.5rem;border-bottom:1px solid var(--studio-border-primary);flex-shrink:0}.studio-select__search input{width:100%;padding:.5rem;border:1px solid var(--studio-border-primary);border-radius:var(--studio-radius-sm);font-family:inherit;font-size:.875rem;color:var(--studio-text-primary);background:var(--studio-bg-primary);outline:none;transition:var(--studio-transition-fast)}.studio-select__search input:focus{border-color:var(--studio-primary);box-shadow:0 0 0 3px var(--studio-primary-bg)}.studio-select__search input::placeholder{color:var(--studio-text-tertiary)}.studio-select__options{overflow-y:auto;flex:1}.studio-select__option{display:flex;align-items:center;gap:.75rem;padding:.625rem .75rem;cursor:pointer;transition:var(--studio-transition-fast);color:var(--studio-text-primary)}.studio-select__option:hover{background:var(--studio-bg-secondary)}.studio-select__option--selected{background:var(--studio-primary-bg);color:var(--studio-primary)}.studio-select__option--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.studio-select__option input[type=checkbox]{margin:0;cursor:pointer}.studio-select__option-icon{flex-shrink:0;color:var(--studio-text-secondary)}.studio-select__option-content{flex:1;min-width:0}.studio-select__option-label{font-size:.875rem;color:inherit}.studio-select__option-description{font-size:.75rem;color:var(--studio-text-secondary);margin-top:.125rem}.studio-select__empty{padding:1rem;text-align:center;color:var(--studio-text-tertiary);font-size:.875rem}.studio-select__hint{font-size:.75rem;color:var(--studio-text-secondary);line-height:1.4}.studio-select__error{display:flex;align-items:center;gap:.25rem;font-size:.75rem;color:var(--studio-error);line-height:1.4}.studio-select__label--floating{position:absolute;top:50%;left:1rem;transform:translateY(-50%);font-size:1rem;color:var(--studio-text-tertiary);pointer-events:none;transition:all var(--studio-transition-fast);background:var(--studio-bg-primary);padding:0 .25rem;z-index:1}:host(.studio-select--focused) .studio-select__label--floating,:host(.studio-select--has-value) .studio-select__label--floating{top:0;font-size:.75rem;color:var(--studio-primary)}:host(.studio-select--error) .studio-select__label--floating{color:var(--studio-error)}:host(.studio-select--outline) .studio-select__control{border:1px solid var(--studio-border-primary);background:var(--studio-bg-primary);border-radius:var(--studio-radius-md)}:host(.studio-select--outline) .studio-select__control:hover:not(:has(.studio-select__trigger:disabled)){border-color:var(--studio-primary)}:host(.studio-select--outline.studio-select--focused) .studio-select__control{border-color:var(--studio-primary);box-shadow:0 0 0 3px var(--studio-primary-bg)}:host(.studio-select--outline.studio-select--error) .studio-select__control{border-color:var(--studio-error)}:host(.studio-select--outline.studio-select--error) .studio-select__control:hover{border-color:var(--studio-error)}:host(.studio-select--outline.studio-select--error.studio-select--focused) .studio-select__control{box-shadow:0 0 0 3px #ef44441a}:host(.studio-select--outline.studio-select--disabled) .studio-select__control{background:var(--studio-bg-secondary);border-color:var(--studio-border-secondary)}:host(.studio-select--filled) .studio-select__control{background:var(--studio-bg-secondary);border:none;border-bottom:2px solid var(--studio-border-primary);border-radius:var(--studio-radius-md) var(--studio-radius-md) 0 0}:host(.studio-select--filled) .studio-select__control:hover:not(:has(.studio-select__trigger:disabled)){background:var(--studio-bg-tertiary)}:host(.studio-select--filled.studio-select--focused) .studio-select__control{border-bottom-color:var(--studio-primary);background:var(--studio-bg-tertiary)}:host(.studio-select--filled.studio-select--error) .studio-select__control{border-bottom-color:var(--studio-error)}:host(.studio-select--filled.studio-select--disabled) .studio-select__control{background:var(--studio-bg-secondary);opacity:.6}:host(.studio-select--underline) .studio-select__control{background:transparent;border:none;border-bottom:1px solid var(--studio-border-primary);border-radius:0}:host(.studio-select--underline) .studio-select__control:hover:not(:has(.studio-select__trigger:disabled)){border-bottom-color:var(--studio-primary)}:host(.studio-select--underline.studio-select--focused) .studio-select__control{border-bottom:2px solid var(--studio-primary)}:host(.studio-select--underline.studio-select--error) .studio-select__control{border-bottom-color:var(--studio-error)}:host(.studio-select--underline.studio-select--disabled) .studio-select__control{border-bottom-style:dashed;opacity:.6}:host(.studio-select--sm) .studio-select__trigger{height:2rem;font-size:.875rem;padding:0 .75rem}:host(.studio-select--sm) .studio-select__prefix{padding-left:.75rem}:host(.studio-select--sm) .studio-select__suffix,:host(.studio-select--sm) .studio-select__clear,:host(.studio-select--sm) .studio-select__loading{padding-right:.5rem}:host(.studio-select--md) .studio-select__trigger{height:2.5rem;font-size:1rem;padding:0 1rem}:host(.studio-select--md) .studio-select__prefix{padding-left:1rem}:host(.studio-select--md) .studio-select__suffix,:host(.studio-select--md) .studio-select__clear,:host(.studio-select--md) .studio-select__loading{padding-right:.75rem}:host(.studio-select--lg) .studio-select__trigger{height:3rem;font-size:1.125rem;padding:0 1.25rem}:host(.studio-select--lg) .studio-select__prefix{padding-left:1.25rem}:host(.studio-select--lg) .studio-select__suffix,:host(.studio-select--lg) .studio-select__clear,:host(.studio-select--lg) .studio-select__loading{padding-right:1rem}:host(.studio-select--radius-none) .studio-select__control{border-radius:0}:host(.studio-select--radius-sm) .studio-select__control{border-radius:var(--studio-radius-sm)}:host(.studio-select--radius-md) .studio-select__control{border-radius:var(--studio-radius-md)}:host(.studio-select--radius-lg) .studio-select__control{border-radius:var(--studio-radius-lg)}:host(.studio-select--radius-full) .studio-select__control{border-radius:9999px}:host(.studio-select--filled.studio-select--radius-none) .studio-select__control{border-radius:0}:host(.studio-select--filled.studio-select--radius-sm) .studio-select__control{border-radius:var(--studio-radius-sm) var(--studio-radius-sm) 0 0}:host(.studio-select--filled.studio-select--radius-md) .studio-select__control{border-radius:var(--studio-radius-md) var(--studio-radius-md) 0 0}:host(.studio-select--filled.studio-select--radius-lg) .studio-select__control{border-radius:var(--studio-radius-lg) var(--studio-radius-lg) 0 0}:host(.studio-select--filled.studio-select--radius-full) .studio-select__control{border-radius:9999px 9999px 0 0}\n"] }]
1611
+ }], ctorParameters: () => [], propDecorators: { triggerEl: [{ type: i0.ViewChild, args: ['triggerButton', { isSignal: true }] }], dropdownEl: [{ type: i0.ViewChild, args: ['dropdownPanel', { isSignal: true }] }], variantInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], sizeInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], radiusInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "radius", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], floatingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "floatingLabel", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], prefixIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefixIcon", required: false }] }], suffixIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffixIcon", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], searchChange: [{ type: i0.Output, args: ["searchChange"] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }], optionSelected: [{ type: i0.Output, args: ["optionSelected"] }] } });
1612
+
1262
1613
  /**
1263
1614
  * Toggle switch component with customizable size and color
1264
1615
  *
@@ -1728,6 +2079,164 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
1728
2079
  * Basic building blocks
1729
2080
  */
1730
2081
 
2082
+ class ButtonToggleGroupComponent {
2083
+ configService = inject(StudioConfigService);
2084
+ toggleGroupDefaults = computed(() => this.configService.config().components?.buttonToggleGroup, ...(ngDevMode ? [{ debugName: "toggleGroupDefaults" }] : []));
2085
+ onChange = () => { };
2086
+ onTouched = () => { };
2087
+ value = model('', ...(ngDevMode ? [{ debugName: "value" }] : []));
2088
+ options = input.required(...(ngDevMode ? [{ debugName: "options" }] : []));
2089
+ multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : []));
2090
+ allowEmpty = input(true, ...(ngDevMode ? [{ debugName: "allowEmpty" }] : []));
2091
+ orientationInput = input(undefined, ...(ngDevMode ? [{ debugName: "orientationInput", alias: 'orientation' }] : [{ alias: 'orientation' }]));
2092
+ sizeInput = input(undefined, ...(ngDevMode ? [{ debugName: "sizeInput", alias: 'size' }] : [{ alias: 'size' }]));
2093
+ variantInput = input(undefined, ...(ngDevMode ? [{ debugName: "variantInput", alias: 'variant' }] : [{ alias: 'variant' }]));
2094
+ colorInput = input(undefined, ...(ngDevMode ? [{ debugName: "colorInput", alias: 'color' }] : [{ alias: 'color' }]));
2095
+ radiusInput = input(undefined, ...(ngDevMode ? [{ debugName: "radiusInput", alias: 'radius' }] : [{ alias: 'radius' }]));
2096
+ shadowInput = input(undefined, ...(ngDevMode ? [{ debugName: "shadowInput", alias: 'shadow' }] : [{ alias: 'shadow' }]));
2097
+ orientation = withConfigDefault(this.orientationInput, computed(() => this.toggleGroupDefaults()?.orientation), 'horizontal');
2098
+ size = withConfigDefault(this.sizeInput, computed(() => this.toggleGroupDefaults()?.size), 'md');
2099
+ variant = withConfigDefault(this.variantInput, computed(() => this.toggleGroupDefaults()?.variant), 'outline');
2100
+ color = withConfigDefault(this.colorInput, computed(() => this.toggleGroupDefaults()?.color), 'primary');
2101
+ radius = withConfigDefault(this.radiusInput, computed(() => this.toggleGroupDefaults()?.radius), 'sm');
2102
+ shadow = withConfigDefault(this.shadowInput, computed(() => this.toggleGroupDefaults()?.shadow), 'none');
2103
+ attached = input(true, ...(ngDevMode ? [{ debugName: "attached" }] : []));
2104
+ fullWidth = input(false, ...(ngDevMode ? [{ debugName: "fullWidth" }] : []));
2105
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
2106
+ spacing = input('sm', ...(ngDevMode ? [{ debugName: "spacing" }] : []));
2107
+ align = input('start', ...(ngDevMode ? [{ debugName: "align" }] : []));
2108
+ showIcons = input(true, ...(ngDevMode ? [{ debugName: "showIcons" }] : []));
2109
+ iconPosition = input('left', ...(ngDevMode ? [{ debugName: "iconPosition" }] : []));
2110
+ ariaLabel = input(...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : []));
2111
+ valueChange = output();
2112
+ hostClasses = computed(() => classNames('studio-button-toggle-group', this.disabled() && 'studio-button-toggle-group--disabled'), ...(ngDevMode ? [{ debugName: "hostClasses" }] : []));
2113
+ isSelected(optionValue) {
2114
+ const currentValue = this.value();
2115
+ if (this.multiple()) {
2116
+ return Array.isArray(currentValue) && currentValue.includes(optionValue);
2117
+ }
2118
+ return currentValue === optionValue;
2119
+ }
2120
+ handleToggle(optionValue) {
2121
+ if (this.disabled())
2122
+ return;
2123
+ let newValue;
2124
+ if (this.multiple()) {
2125
+ const currentArray = Array.isArray(this.value()) ? this.value() : [];
2126
+ const index = currentArray.indexOf(optionValue);
2127
+ if (index > -1) {
2128
+ if (!this.allowEmpty() && currentArray.length === 1) {
2129
+ return;
2130
+ }
2131
+ newValue = currentArray.filter(v => v !== optionValue);
2132
+ }
2133
+ else {
2134
+ newValue = [...currentArray, optionValue];
2135
+ }
2136
+ }
2137
+ else {
2138
+ const currentValue = this.value();
2139
+ if (currentValue === optionValue && !this.allowEmpty()) {
2140
+ return;
2141
+ }
2142
+ newValue = currentValue === optionValue ? (this.allowEmpty() ? '' : optionValue) : optionValue;
2143
+ }
2144
+ this.value.set(newValue);
2145
+ this.onChange(newValue);
2146
+ this.onTouched();
2147
+ this.valueChange.emit(newValue);
2148
+ }
2149
+ writeValue(val) {
2150
+ if (val !== undefined && val !== null) {
2151
+ this.value.set(val);
2152
+ }
2153
+ else {
2154
+ this.value.set(this.multiple() ? [] : '');
2155
+ }
2156
+ }
2157
+ registerOnChange(fn) {
2158
+ this.onChange = fn;
2159
+ }
2160
+ registerOnTouched(fn) {
2161
+ this.onTouched = fn;
2162
+ }
2163
+ setDisabledState(isDisabled) {
2164
+ // disabled handled via input signal
2165
+ }
2166
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: ButtonToggleGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2167
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: ButtonToggleGroupComponent, isStandalone: true, selector: "studio-button-toggle-group", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, allowEmpty: { classPropertyName: "allowEmpty", publicName: "allowEmpty", isSignal: true, isRequired: false, transformFunction: null }, orientationInput: { classPropertyName: "orientationInput", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, sizeInput: { classPropertyName: "sizeInput", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, variantInput: { classPropertyName: "variantInput", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, colorInput: { classPropertyName: "colorInput", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, radiusInput: { classPropertyName: "radiusInput", publicName: "radius", isSignal: true, isRequired: false, transformFunction: null }, shadowInput: { classPropertyName: "shadowInput", publicName: "shadow", isSignal: true, isRequired: false, transformFunction: null }, attached: { classPropertyName: "attached", publicName: "attached", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, spacing: { classPropertyName: "spacing", publicName: "spacing", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, showIcons: { classPropertyName: "showIcons", publicName: "showIcons", isSignal: true, isRequired: false, transformFunction: null }, iconPosition: { classPropertyName: "iconPosition", publicName: "iconPosition", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", valueChange: "valueChange" }, host: { properties: { "class": "hostClasses()" } }, providers: [
2168
+ {
2169
+ provide: NG_VALUE_ACCESSOR,
2170
+ useExisting: forwardRef(() => ButtonToggleGroupComponent),
2171
+ multi: true
2172
+ }
2173
+ ], ngImport: i0, template: `
2174
+ <studio-button-group
2175
+ [orientation]="orientation()"
2176
+ [attached]="attached()"
2177
+ [fullWidth]="fullWidth()"
2178
+ [spacing]="spacing()"
2179
+ [align]="align()"
2180
+ [ariaLabel]="ariaLabel()">
2181
+ @for (option of options(); track option.value) {
2182
+ <studio-button
2183
+ [variant]="isSelected(option.value) ? 'solid' : variant()"
2184
+ [size]="size()"
2185
+ [color]="isSelected(option.value) ? color() : 'secondary'"
2186
+ [radius]="radius()"
2187
+ [shadow]="shadow()"
2188
+ [disabled]="disabled() || option.disabled || false"
2189
+ [icon]="showIcons() ? option.icon : undefined"
2190
+ [iconPosition]="iconPosition()"
2191
+ [ariaLabel]="option.ariaLabel"
2192
+ (clicked)="handleToggle(option.value)">
2193
+ @if (iconPosition() !== 'only') {
2194
+ {{ option.label }}
2195
+ }
2196
+ </studio-button>
2197
+ }
2198
+ </studio-button-group>
2199
+ `, isInline: true, styles: [":host{display:inline-block}:host(.studio-button-toggle-group--disabled){opacity:.6;pointer-events:none}\n"], dependencies: [{ kind: "component", type: ButtonComponent, selector: "studio-button", inputs: ["variant", "size", "color", "radius", "shadow", "compact", "disabled", "loading", "loadingText", "fullWidth", "type", "icon", "iconPosition", "href", "target", "badge", "badgeColor", "ariaLabel"], outputs: ["clicked"] }, { kind: "component", type: ButtonGroupComponent, selector: "studio-button-group", inputs: ["orientation", "size", "variant", "color", "radius", "attached", "fullWidth", "disabled", "spacing", "align", "ariaLabel", "role"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2200
+ }
2201
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: ButtonToggleGroupComponent, decorators: [{
2202
+ type: Component,
2203
+ args: [{ selector: 'studio-button-toggle-group', standalone: true, imports: [ButtonComponent, ButtonGroupComponent], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
2204
+ {
2205
+ provide: NG_VALUE_ACCESSOR,
2206
+ useExisting: forwardRef(() => ButtonToggleGroupComponent),
2207
+ multi: true
2208
+ }
2209
+ ], host: {
2210
+ '[class]': 'hostClasses()'
2211
+ }, template: `
2212
+ <studio-button-group
2213
+ [orientation]="orientation()"
2214
+ [attached]="attached()"
2215
+ [fullWidth]="fullWidth()"
2216
+ [spacing]="spacing()"
2217
+ [align]="align()"
2218
+ [ariaLabel]="ariaLabel()">
2219
+ @for (option of options(); track option.value) {
2220
+ <studio-button
2221
+ [variant]="isSelected(option.value) ? 'solid' : variant()"
2222
+ [size]="size()"
2223
+ [color]="isSelected(option.value) ? color() : 'secondary'"
2224
+ [radius]="radius()"
2225
+ [shadow]="shadow()"
2226
+ [disabled]="disabled() || option.disabled || false"
2227
+ [icon]="showIcons() ? option.icon : undefined"
2228
+ [iconPosition]="iconPosition()"
2229
+ [ariaLabel]="option.ariaLabel"
2230
+ (clicked)="handleToggle(option.value)">
2231
+ @if (iconPosition() !== 'only') {
2232
+ {{ option.label }}
2233
+ }
2234
+ </studio-button>
2235
+ }
2236
+ </studio-button-group>
2237
+ `, styles: [":host{display:inline-block}:host(.studio-button-toggle-group--disabled){opacity:.6;pointer-events:none}\n"] }]
2238
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], allowEmpty: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowEmpty", required: false }] }], orientationInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], sizeInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], variantInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], colorInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], radiusInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "radius", required: false }] }], shadowInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "shadow", required: false }] }], attached: [{ type: i0.Input, args: [{ isSignal: true, alias: "attached", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], spacing: [{ type: i0.Input, args: [{ isSignal: true, alias: "spacing", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], showIcons: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIcons", required: false }] }], iconPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconPosition", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
2239
+
1731
2240
  /**
1732
2241
  * Theme toggle switch with sun/moon icons
1733
2242
  *
@@ -1818,6 +2327,433 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
1818
2327
  * Domain-specific components for educational platforms
1819
2328
  */
1820
2329
 
2330
+ class MaskEngine {
2331
+ config;
2332
+ tokens;
2333
+ patterns = {
2334
+ '9': /\d/,
2335
+ 'A': /[a-zA-Zа-яА-Я]/,
2336
+ '*': /[a-zA-Z0-9]/,
2337
+ '#': /./,
2338
+ 'X': /[a-zA-Z0-9а-яА-Я]/,
2339
+ };
2340
+ constructor(config) {
2341
+ this.config = config;
2342
+ this.tokens = this.parsePattern(config.pattern);
2343
+ }
2344
+ process(value, cursorPos) {
2345
+ if (!value) {
2346
+ return {
2347
+ value: '',
2348
+ raw: '',
2349
+ cursor: 0,
2350
+ isComplete: false
2351
+ };
2352
+ }
2353
+ const cleaned = this.extractRaw(value);
2354
+ const formatted = this.applyMask(cleaned);
2355
+ const newCursor = cursorPos !== undefined
2356
+ ? this.calculateCursor(cursorPos, value, formatted)
2357
+ : formatted.length;
2358
+ const displayValue = this.config.guide && this.config.showOnFocus
2359
+ ? this.applyGuide(formatted)
2360
+ : formatted;
2361
+ return {
2362
+ value: displayValue,
2363
+ raw: cleaned,
2364
+ cursor: newCursor,
2365
+ isComplete: this.checkComplete(cleaned)
2366
+ };
2367
+ }
2368
+ getPlaceholder() {
2369
+ if (!this.tokens || this.tokens.length === 0)
2370
+ return '';
2371
+ return this.tokens
2372
+ .map(token => {
2373
+ if (token.type === 'static' && token.char)
2374
+ return token.char;
2375
+ return this.config.placeholder || '_';
2376
+ })
2377
+ .join('');
2378
+ }
2379
+ parsePattern(pattern) {
2380
+ const tokens = [];
2381
+ let i = 0;
2382
+ while (i < pattern.length) {
2383
+ const char = pattern[i];
2384
+ if (char === '\\' && i + 1 < pattern.length) {
2385
+ tokens.push({
2386
+ type: 'static',
2387
+ char: pattern[i + 1]
2388
+ });
2389
+ i += 2;
2390
+ continue;
2391
+ }
2392
+ if (char === '?' && tokens.length > 0) {
2393
+ tokens[tokens.length - 1].optional = true;
2394
+ i++;
2395
+ continue;
2396
+ }
2397
+ if (char in this.patterns) {
2398
+ tokens.push({
2399
+ type: this.getTokenType(char),
2400
+ pattern: this.patterns[char]
2401
+ });
2402
+ i++;
2403
+ }
2404
+ else {
2405
+ tokens.push({
2406
+ type: 'static',
2407
+ char
2408
+ });
2409
+ i++;
2410
+ }
2411
+ }
2412
+ return tokens;
2413
+ }
2414
+ getTokenType(char) {
2415
+ const typeMap = {
2416
+ '9': 'digit',
2417
+ 'A': 'letter',
2418
+ '*': 'alphanumeric',
2419
+ '#': 'any',
2420
+ 'X': 'mixed'
2421
+ };
2422
+ return typeMap[char] || 'any';
2423
+ }
2424
+ applyMask(raw) {
2425
+ if (!raw || !this.tokens)
2426
+ return '';
2427
+ let result = '';
2428
+ let rawIndex = 0;
2429
+ for (const token of this.tokens) {
2430
+ if (rawIndex >= raw.length) {
2431
+ break;
2432
+ }
2433
+ if (token.type === 'static' && token.char) {
2434
+ result += token.char;
2435
+ if (raw[rawIndex] === token.char) {
2436
+ rawIndex++;
2437
+ }
2438
+ }
2439
+ else {
2440
+ const char = raw[rawIndex];
2441
+ if (token.pattern?.test(char)) {
2442
+ result += char;
2443
+ rawIndex++;
2444
+ }
2445
+ else if (!token.optional) {
2446
+ break;
2447
+ }
2448
+ }
2449
+ }
2450
+ return result;
2451
+ }
2452
+ extractRaw(value) {
2453
+ if (!value || !this.tokens)
2454
+ return '';
2455
+ const staticChars = this.tokens
2456
+ .filter(t => t.type === 'static' && t.char)
2457
+ .map(t => t.char)
2458
+ .filter(Boolean);
2459
+ return value
2460
+ .split('')
2461
+ .filter(char => !staticChars.includes(char))
2462
+ .join('');
2463
+ }
2464
+ applyGuide(value) {
2465
+ if (!this.tokens || this.tokens.length === 0)
2466
+ return value;
2467
+ const placeholder = this.config.placeholder || '_';
2468
+ let result = value;
2469
+ const currentLength = value.replace(/[^a-zA-Z0-9а-яА-Я]/g, '').length;
2470
+ let addedCount = 0;
2471
+ for (let i = 0; i < this.tokens.length; i++) {
2472
+ const token = this.tokens[i];
2473
+ if (addedCount >= currentLength) {
2474
+ if (token.type === 'static' && token.char) {
2475
+ result += token.char;
2476
+ }
2477
+ else {
2478
+ result += placeholder;
2479
+ addedCount++;
2480
+ }
2481
+ }
2482
+ }
2483
+ return result;
2484
+ }
2485
+ calculateCursor(oldCursor, oldValue, newValue) {
2486
+ const diff = newValue.length - oldValue.length;
2487
+ let newCursor = oldCursor + diff;
2488
+ while (newCursor < newValue.length) {
2489
+ const charAtCursor = newValue[newCursor];
2490
+ const isStatic = this.tokens.some((t, i) => t.type === 'static' && t.char === charAtCursor && i === newCursor);
2491
+ if (!isStatic)
2492
+ break;
2493
+ newCursor++;
2494
+ }
2495
+ return Math.max(0, Math.min(newValue.length, newCursor));
2496
+ }
2497
+ checkComplete(raw) {
2498
+ const requiredTokens = this.tokens.filter(t => t.type !== 'static' && !t.optional);
2499
+ return raw.length >= requiredTokens.length;
2500
+ }
2501
+ }
2502
+
2503
+ const MASK_PRESETS = {
2504
+ 'phone': {
2505
+ pattern: '+7 (999) 999-99-99',
2506
+ placeholder: '_',
2507
+ guide: true
2508
+ },
2509
+ 'phone-ru': {
2510
+ pattern: '+7 (999) 999-99-99',
2511
+ placeholder: '_',
2512
+ guide: true
2513
+ },
2514
+ 'phone-kz': {
2515
+ pattern: '+7 (999) 999-99-99',
2516
+ placeholder: '_',
2517
+ guide: true
2518
+ },
2519
+ 'date': {
2520
+ pattern: '99.99.9999',
2521
+ placeholder: '_',
2522
+ guide: true
2523
+ },
2524
+ 'datetime': {
2525
+ pattern: '99.99.9999 99:99',
2526
+ placeholder: '_',
2527
+ guide: true
2528
+ },
2529
+ 'time': {
2530
+ pattern: '99:99',
2531
+ placeholder: '_',
2532
+ guide: true
2533
+ },
2534
+ 'card': {
2535
+ pattern: '9999 9999 9999 9999',
2536
+ placeholder: 'X',
2537
+ guide: true
2538
+ },
2539
+ 'inn': {
2540
+ pattern: '999999999999',
2541
+ placeholder: '_',
2542
+ guide: false
2543
+ },
2544
+ 'snils': {
2545
+ pattern: '999-999-999 99',
2546
+ placeholder: '_',
2547
+ guide: true
2548
+ },
2549
+ 'passport-ru': {
2550
+ pattern: '99 99 999999',
2551
+ placeholder: '_',
2552
+ guide: true
2553
+ }
2554
+ };
2555
+
2556
+ class MaskDirective {
2557
+ el = inject((ElementRef));
2558
+ renderer = inject(Renderer2);
2559
+ destroyRef = inject(DestroyRef);
2560
+ mask = input('', ...(ngDevMode ? [{ debugName: "mask", alias: 'studioMask',
2561
+ transform: (value) => {
2562
+ if (!value)
2563
+ return '';
2564
+ if (value in MASK_PRESETS) {
2565
+ return MASK_PRESETS[value].pattern;
2566
+ }
2567
+ return value;
2568
+ } }] : [{
2569
+ alias: 'studioMask',
2570
+ transform: (value) => {
2571
+ if (!value)
2572
+ return '';
2573
+ if (value in MASK_PRESETS) {
2574
+ return MASK_PRESETS[value].pattern;
2575
+ }
2576
+ return value;
2577
+ }
2578
+ }]));
2579
+ maskPlaceholder = input('_', ...(ngDevMode ? [{ debugName: "maskPlaceholder" }] : []));
2580
+ maskGuide = input(true, ...(ngDevMode ? [{ debugName: "maskGuide" }] : []));
2581
+ maskShowOnHover = input(false, ...(ngDevMode ? [{ debugName: "maskShowOnHover" }] : []));
2582
+ maskShowOnFocus = input(true, ...(ngDevMode ? [{ debugName: "maskShowOnFocus" }] : []));
2583
+ maskClearIncomplete = input(false, ...(ngDevMode ? [{ debugName: "maskClearIncomplete" }] : []));
2584
+ maskAutoUnmask = input(false, ...(ngDevMode ? [{ debugName: "maskAutoUnmask" }] : []));
2585
+ config = computed(() => ({
2586
+ pattern: this.mask(),
2587
+ placeholder: this.maskPlaceholder(),
2588
+ guide: this.maskGuide(),
2589
+ showOnHover: this.maskShowOnHover(),
2590
+ showOnFocus: this.maskShowOnFocus(),
2591
+ clearIncomplete: this.maskClearIncomplete(),
2592
+ autoUnmask: this.maskAutoUnmask()
2593
+ }), ...(ngDevMode ? [{ debugName: "config" }] : []));
2594
+ engine = signal(null, ...(ngDevMode ? [{ debugName: "engine" }] : []));
2595
+ isComplete = signal(false, ...(ngDevMode ? [{ debugName: "isComplete" }] : []));
2596
+ isActive = signal(false, ...(ngDevMode ? [{ debugName: "isActive" }] : []));
2597
+ onChange = () => { };
2598
+ onTouched = () => { };
2599
+ constructor() {
2600
+ effect(() => {
2601
+ const maskPattern = this.mask();
2602
+ if (!maskPattern)
2603
+ return;
2604
+ const cfg = this.config();
2605
+ const newEngine = new MaskEngine(cfg);
2606
+ this.engine.set(newEngine);
2607
+ this.updatePlaceholder();
2608
+ });
2609
+ }
2610
+ onInput(event) {
2611
+ const eng = this.engine();
2612
+ if (!eng)
2613
+ return;
2614
+ const element = this.el.nativeElement;
2615
+ const cursorPos = element.selectionStart ?? 0;
2616
+ const result = eng.process(element.value, cursorPos);
2617
+ if (element.value !== result.value) {
2618
+ this.renderer.setProperty(element, 'value', result.value);
2619
+ element.setSelectionRange(result.cursor, result.cursor);
2620
+ }
2621
+ this.isComplete.set(result.isComplete);
2622
+ const outputValue = this.config().autoUnmask ? result.raw : result.value;
2623
+ this.onChange(outputValue);
2624
+ }
2625
+ onFocus() {
2626
+ this.isActive.set(true);
2627
+ if (this.maskShowOnFocus() && this.maskGuide()) {
2628
+ const eng = this.engine();
2629
+ if (!eng)
2630
+ return;
2631
+ const element = this.el.nativeElement;
2632
+ if (!element.value) {
2633
+ const result = eng.process('', 0);
2634
+ this.renderer.setProperty(element, 'value', result.value);
2635
+ element.setSelectionRange(0, 0);
2636
+ }
2637
+ }
2638
+ }
2639
+ onBlur() {
2640
+ this.isActive.set(false);
2641
+ this.onTouched();
2642
+ if (this.maskClearIncomplete() && !this.isComplete()) {
2643
+ this.renderer.setProperty(this.el.nativeElement, 'value', '');
2644
+ this.onChange('');
2645
+ }
2646
+ }
2647
+ onPaste(event) {
2648
+ event.preventDefault();
2649
+ const eng = this.engine();
2650
+ if (!eng)
2651
+ return;
2652
+ const pastedText = event.clipboardData?.getData('text') || '';
2653
+ const element = this.el.nativeElement;
2654
+ const cursorPos = element.selectionStart ?? 0;
2655
+ const currentValue = element.value;
2656
+ const before = currentValue.slice(0, cursorPos);
2657
+ const after = currentValue.slice(element.selectionEnd ?? cursorPos);
2658
+ const newValue = before + pastedText + after;
2659
+ const result = eng.process(newValue, cursorPos + pastedText.length);
2660
+ this.renderer.setProperty(element, 'value', result.value);
2661
+ element.setSelectionRange(result.cursor, result.cursor);
2662
+ const outputValue = this.config().autoUnmask ? result.raw : result.value;
2663
+ this.onChange(outputValue);
2664
+ }
2665
+ writeValue(value) {
2666
+ const eng = this.engine();
2667
+ if (!eng)
2668
+ return;
2669
+ if (value) {
2670
+ const result = eng.process(value);
2671
+ this.renderer.setProperty(this.el.nativeElement, 'value', result.value);
2672
+ this.isComplete.set(result.isComplete);
2673
+ }
2674
+ else {
2675
+ this.renderer.setProperty(this.el.nativeElement, 'value', '');
2676
+ this.isComplete.set(false);
2677
+ }
2678
+ }
2679
+ registerOnChange(fn) {
2680
+ this.onChange = fn;
2681
+ }
2682
+ registerOnTouched(fn) {
2683
+ this.onTouched = fn;
2684
+ }
2685
+ validate(control) {
2686
+ if (!control.value)
2687
+ return null;
2688
+ const complete = this.isComplete();
2689
+ return complete ? null : { maskIncomplete: { value: control.value } };
2690
+ }
2691
+ updatePlaceholder() {
2692
+ if (!this.maskShowOnFocus())
2693
+ return;
2694
+ const eng = this.engine();
2695
+ if (!eng)
2696
+ return;
2697
+ try {
2698
+ const placeholderText = eng.getPlaceholder();
2699
+ if (placeholderText) {
2700
+ this.renderer.setAttribute(this.el.nativeElement, 'placeholder', placeholderText);
2701
+ }
2702
+ }
2703
+ catch (e) {
2704
+ console.warn('Failed to update placeholder:', e);
2705
+ }
2706
+ }
2707
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: MaskDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2708
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.12", type: MaskDirective, isStandalone: true, selector: "input[studioMask]", inputs: { mask: { classPropertyName: "mask", publicName: "studioMask", isSignal: true, isRequired: false, transformFunction: null }, maskPlaceholder: { classPropertyName: "maskPlaceholder", publicName: "maskPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, maskGuide: { classPropertyName: "maskGuide", publicName: "maskGuide", isSignal: true, isRequired: false, transformFunction: null }, maskShowOnHover: { classPropertyName: "maskShowOnHover", publicName: "maskShowOnHover", isSignal: true, isRequired: false, transformFunction: null }, maskShowOnFocus: { classPropertyName: "maskShowOnFocus", publicName: "maskShowOnFocus", isSignal: true, isRequired: false, transformFunction: null }, maskClearIncomplete: { classPropertyName: "maskClearIncomplete", publicName: "maskClearIncomplete", isSignal: true, isRequired: false, transformFunction: null }, maskAutoUnmask: { classPropertyName: "maskAutoUnmask", publicName: "maskAutoUnmask", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "input": "onInput($event)", "focus": "onFocus()", "blur": "onBlur()", "paste": "onPaste($event)" }, properties: { "attr.data-mask-complete": "isComplete()", "attr.data-mask-active": "isActive()" } }, providers: [
2709
+ {
2710
+ provide: NG_VALUE_ACCESSOR,
2711
+ useExisting: forwardRef(() => MaskDirective),
2712
+ multi: true
2713
+ },
2714
+ {
2715
+ provide: NG_VALIDATORS,
2716
+ useExisting: forwardRef(() => MaskDirective),
2717
+ multi: true
2718
+ }
2719
+ ], ngImport: i0 });
2720
+ }
2721
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: MaskDirective, decorators: [{
2722
+ type: Directive,
2723
+ args: [{
2724
+ selector: 'input[studioMask]',
2725
+ standalone: true,
2726
+ providers: [
2727
+ {
2728
+ provide: NG_VALUE_ACCESSOR,
2729
+ useExisting: forwardRef(() => MaskDirective),
2730
+ multi: true
2731
+ },
2732
+ {
2733
+ provide: NG_VALIDATORS,
2734
+ useExisting: forwardRef(() => MaskDirective),
2735
+ multi: true
2736
+ }
2737
+ ],
2738
+ host: {
2739
+ '[attr.data-mask-complete]': 'isComplete()',
2740
+ '[attr.data-mask-active]': 'isActive()'
2741
+ }
2742
+ }]
2743
+ }], ctorParameters: () => [], propDecorators: { mask: [{ type: i0.Input, args: [{ isSignal: true, alias: "studioMask", required: false }] }], maskPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "maskPlaceholder", required: false }] }], maskGuide: [{ type: i0.Input, args: [{ isSignal: true, alias: "maskGuide", required: false }] }], maskShowOnHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "maskShowOnHover", required: false }] }], maskShowOnFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "maskShowOnFocus", required: false }] }], maskClearIncomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "maskClearIncomplete", required: false }] }], maskAutoUnmask: [{ type: i0.Input, args: [{ isSignal: true, alias: "maskAutoUnmask", required: false }] }], onInput: [{
2744
+ type: HostListener,
2745
+ args: ['input', ['$event']]
2746
+ }], onFocus: [{
2747
+ type: HostListener,
2748
+ args: ['focus']
2749
+ }], onBlur: [{
2750
+ type: HostListener,
2751
+ args: ['blur']
2752
+ }], onPaste: [{
2753
+ type: HostListener,
2754
+ args: ['paste', ['$event']]
2755
+ }] } });
2756
+
1821
2757
  /**
1822
2758
  * Public API Surface of @eduboxpro/studio
1823
2759
  */
@@ -1829,5 +2765,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
1829
2765
  * Generated bundle index. Do not edit.
1830
2766
  */
1831
2767
 
1832
- export { BadgeComponent, ButtonComponent, CheckboxComponent, IconComponent, InputComponent, STUDIO_CONFIG, StudioConfigService, SwitchComponent, TextareaComponent, ThemeSwitchComponent, classNames, isSafeUrl, loadGoogleFont, loadGoogleFonts, provideStudioConfig, provideStudioIcons, sanitizeUrl, withConfigDefault };
2768
+ export { BadgeComponent, ButtonComponent, ButtonGroupComponent, ButtonToggleGroupComponent, CheckboxComponent, IconComponent, InputComponent, MASK_PRESETS, MaskDirective, MaskEngine, STUDIO_CONFIG, SelectComponent, StudioConfigService, SwitchComponent, TextareaComponent, ThemeSwitchComponent, classNames, isSafeUrl, loadGoogleFont, loadGoogleFonts, provideStudioConfig, provideStudioIcons, sanitizeUrl, withConfigDefault };
1833
2769
  //# sourceMappingURL=eduboxpro-studio.mjs.map