@edmicro/olkit 1.0.20 → 1.1.1

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,5 +1,5 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { OnInit, AfterContentInit, OnDestroy, ElementRef, QueryList, TemplateRef, WritableSignal } from '@angular/core';
2
+ import { OnInit, AfterContentInit, OnDestroy, TemplateRef, ElementRef, QueryList, WritableSignal } from '@angular/core';
3
3
  import { ControlValueAccessor } from '@angular/forms';
4
4
  import * as _edmicro_olkit from '@edmicro/olkit';
5
5
  import { SafeHtml } from '@angular/platform-browser';
@@ -110,13 +110,13 @@ declare class OlAlert {
110
110
  * phần chữ cho `warning` thì icon và nút đóng bị bỏ quên — icon trắng trên
111
111
  * nền `#FFC022` tương phản **1.64**, gần như không nhìn thấy.
112
112
  */
113
- private readonly solidNenSang;
113
+ private readonly solidHasLightBg;
114
114
  /**
115
115
  * Nền solid nào SÁNG LÊN ở chế độ tối đến mức phải lật chữ. Chỉ `success`:
116
116
  * đo được trắng 3.65 / đen 5.75 trên nền `#009966`. `danger` ở dark vẫn tối
117
117
  * (trắng 6.03) nên giữ nguyên.
118
118
  */
119
- private readonly solidNenSangODark;
119
+ private readonly solidHasLightBgInDark;
120
120
  readonly iconClasses: _angular_core.Signal<string>;
121
121
  /** Nhãn tròn của layout `small`. */
122
122
  readonly badgeClasses: _angular_core.Signal<"text-xs font-semibold px-2.5 py-1 rounded-full me-3 shrink-0 bg-success-medium text-fg-success-strong" | "text-xs font-semibold px-2.5 py-1 rounded-full me-3 shrink-0 bg-warning-medium text-fg-warning-strong" | "text-xs font-semibold px-2.5 py-1 rounded-full me-3 shrink-0 bg-danger-medium text-fg-danger-strong" | "text-xs font-semibold px-2.5 py-1 rounded-full me-3 shrink-0 bg-brand-soft text-fg-brand-strong" | "text-xs font-semibold px-2.5 py-1 rounded-full me-3 shrink-0 bg-neutral-quaternary text-heading">;
@@ -239,8 +239,9 @@ declare class OlScrollArea {
239
239
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlScrollArea, "ol-scroll-area", never, { "scrollbar": { "alias": "scrollbar"; "required": false; "isSignal": true; }; "axis": { "alias": "axis"; "required": false; "isSignal": true; }; "smooth": { "alias": "smooth"; "required": false; "isSignal": true; }; "innerClass": { "alias": "innerClass"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
240
240
  }
241
241
 
242
- type ButtonColor = 'default' | 'brand' | 'primary' | 'alternative' | 'secondary' | 'tertiary' | 'ghost' | 'success' | 'green' | 'danger' | 'red' | 'warning' | 'yellow' | 'purple' | 'dark' | 'light';
243
- type ButtonSize = 'xs' | 'sm' | 'base' | 'lg' | 'xl';
242
+ type ButtonColor = 'default' | 'brand' | 'primary' | 'alternative' | 'secondary' | 'tertiary' | 'ghost' | 'success' | 'green' | 'danger' | 'red' | 'warning' | 'yellow' | 'dark' | 'light';
243
+ type ButtonSize = 'xs' | 'sm' | 'base' | 'l' | 'xl';
244
+ type OlButtonType = 'button' | 'submit' | 'reset';
244
245
  declare class OlButton {
245
246
  readonly color: _angular_core.InputSignal<ButtonColor>;
246
247
  readonly size: _angular_core.InputSignal<ButtonSize>;
@@ -249,7 +250,7 @@ declare class OlButton {
249
250
  readonly iconOnly: _angular_core.InputSignal<boolean>;
250
251
  readonly loading: _angular_core.InputSignal<boolean>;
251
252
  readonly disabled: _angular_core.InputSignal<boolean>;
252
- readonly type: _angular_core.InputSignal<"reset" | "submit" | "button">;
253
+ readonly type: _angular_core.InputSignal<OlButtonType>;
253
254
  /**
254
255
  * Tên đọc được cho nút **chỉ có icon**. Không có nó thì trình đọc màn hình
255
256
  * đọc ra một nút trống — đo DOM 12 trang docs từng ra 48 nút như vậy.
@@ -396,10 +397,17 @@ type EmptySize = 'sm' | 'base';
396
397
  /**
397
398
  * Trạng thái rỗng — không có dữ liệu, không có kết quả tìm kiếm.
398
399
  *
399
- * Chưa có node Figma. Hình dạng rút từ chỗ duy nhất trong thư viện đang tự dựng
400
- * trạng thái rỗng: `ol-autocomplete` (`p-3 text-center text-sm text-body-subtle`
401
- * kèm một nút hành động). Đó là bản `sm`, dùng bên trong bảng thả xuống. Bản
402
- * `base` là cho giữa trang, chữ và khoảng cách lớn hơn.
400
+ * Bản `base` theo Figma `ol-table-empty` (trang ⤷ Tables, đọc 2026-09-15):
401
+ * padding dọc 64px ngang 24px, khoảng cách 8px, tiêu đề 18px Semi Bold,
402
+ * mô tả 14px Regular, nút hành động 40px.
403
+ *
404
+ * Figma vẽ khung biểu tượng 56px tròn nền `bg-neutral-secondary-medium` bằng
405
+ * frame tự vẽ, KHÔNG dùng `ol-icon-shape` — mà thang của component đó là
406
+ * 16·24·36·48·64·96 nên không có 56. Dùng `lg` (48px), gần nhất mà vẫn là
407
+ * component thật; đừng bịa cỡ 56 chỉ để khớp một frame vẽ tay.
408
+ *
409
+ * Bản `sm` rút từ `ol-autocomplete` (`p-3 text-center text-sm text-body-subtle`),
410
+ * dùng bên trong bảng thả xuống.
403
411
  *
404
412
  * Nút hành động đi qua slot `[actions]` để nơi gọi tự đặt `ol-button` — cùng
405
413
  * lối với slot `[tags]` của `ol-helper`.
@@ -410,7 +418,7 @@ declare class OlEmpty {
410
418
  readonly description: _angular_core.InputSignal<string>;
411
419
  readonly size: _angular_core.InputSignal<EmptySize>;
412
420
  readonly wrapperClasses: _angular_core.Signal<string>;
413
- readonly titleClasses: _angular_core.Signal<"text-sm font-medium text-heading" | "text-base font-semibold text-heading">;
421
+ readonly titleClasses: _angular_core.Signal<"text-sm font-medium text-heading" | "text-lg font-semibold text-heading">;
414
422
  readonly iconSize: _angular_core.Signal<IconShapeSize>;
415
423
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlEmpty, never>;
416
424
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlEmpty, "ol-empty", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, {}, never, ["[actions]"], true, never>;
@@ -419,6 +427,7 @@ declare class OlEmpty {
419
427
  type AvatarSize = '2xs' | 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl';
420
428
  type AvatarShape = 'rounded' | 'circle';
421
429
  type AvatarStatus = 'online' | 'offline' | 'alternative' | 'busy' | 'away';
430
+ type OlAvatarStatusPosition = 'bottom-right' | 'top-right';
422
431
  declare class OlAvatar {
423
432
  readonly src: _angular_core.InputSignal<string>;
424
433
  readonly alt: _angular_core.InputSignal<string>;
@@ -428,12 +437,28 @@ declare class OlAvatar {
428
437
  readonly shape: _angular_core.InputSignal<AvatarShape>;
429
438
  readonly bordered: _angular_core.InputSignal<boolean>;
430
439
  readonly status: _angular_core.InputSignal<AvatarStatus | null>;
431
- readonly statusPosition: _angular_core.InputSignal<"top-right" | "bottom-right">;
440
+ readonly statusPosition: _angular_core.InputSignal<OlAvatarStatusPosition>;
432
441
  readonly removable: _angular_core.InputSignal<boolean>;
433
442
  readonly name: _angular_core.InputSignal<string>;
434
443
  readonly helper: _angular_core.InputSignal<string>;
435
444
  readonly showHelper: _angular_core.InputSignal<boolean>;
436
445
  readonly remove: _angular_core.OutputEmitterRef<MouseEvent>;
446
+ /**
447
+ * Cỡ icon placeholder, tính theo px để BÁM cỡ avatar.
448
+ *
449
+ * Trước đây template ghi cứng `size="sm"` (16px) nên icon đứng yên ở mọi cỡ
450
+ * avatar — đo /avatar ngày 2026-09-16: 17 avatar có icon cho ra SÁU tỷ lệ
451
+ * khác nhau, từ 0,889 (avatar 18px) xuống 0,250 (avatar 64px).
452
+ *
453
+ * Figma KHÔNG có tỷ lệ để chép: biến thể `Type=Placeholder` dùng chữ viết
454
+ * tắt ("PH") chứ không phải icon. Nên lấy tỷ lệ 0,5 của cỡ `base` hiện tại
455
+ * làm chuẩn và áp cho cả thang — đó là cỡ dùng nhiều nhất, và là tỷ lệ duy
456
+ * nhất trong sáu cái trông cân đối.
457
+ *
458
+ * Dùng SỐ px chứ không phải tên cỡ: thang `IconSize` (14·16·20·24·32·40·48)
459
+ * quá thưa ở đầu nhỏ, ép `xs` (avatar 18px) phải lấy icon 14px — tỷ lệ 0,778.
460
+ */
461
+ protected readonly iconPx: _angular_core.Signal<number>;
437
462
  onRemoveClick(event: MouseEvent): void;
438
463
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlAvatar, never>;
439
464
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlAvatar, "ol-avatar", never, { "src": { "alias": "src"; "required": false; "isSignal": true; }; "alt": { "alias": "alt"; "required": false; "isSignal": true; }; "text": { "alias": "text"; "required": false; "isSignal": true; }; "initials": { "alias": "initials"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "shape": { "alias": "shape"; "required": false; "isSignal": true; }; "bordered": { "alias": "bordered"; "required": false; "isSignal": true; }; "status": { "alias": "status"; "required": false; "isSignal": true; }; "statusPosition": { "alias": "statusPosition"; "required": false; "isSignal": true; }; "removable": { "alias": "removable"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "helper": { "alias": "helper"; "required": false; "isSignal": true; }; "showHelper": { "alias": "showHelper"; "required": false; "isSignal": true; }; }, { "remove": "remove"; }, never, ["*"], true, never>;
@@ -448,7 +473,7 @@ declare class OlAvatarGroup {
448
473
  }
449
474
 
450
475
  type BadgeTheme = 'brand' | 'gray' | 'white' | 'danger' | 'warning' | 'success' | 'default' | 'dark' | 'red' | 'yellow' | 'green';
451
- type BadgeSize = 'sm' | 'lg' | 'xs';
476
+ type BadgeSize = 'xs' | 'sm' | 'lg';
452
477
  type BadgeShape = 'square' | 'pill';
453
478
  /**
454
479
  * Figma `ol-badge` #1146:22250 trục `Type` = `Default | With avatar | With dot |
@@ -650,7 +675,7 @@ declare class OlCarousel implements AfterContentInit {
650
675
  * để ghi đè.
651
676
  */
652
677
  readonly slideCountInput: _angular_core.InputSignal<number>;
653
- private readonly soConDaDem;
678
+ private readonly countedSlides;
654
679
  /**
655
680
  * Làm sạch TRƯỚC khi ai đó dựng mảng từ nó.
656
681
  *
@@ -674,10 +699,33 @@ declare class OlCarousel implements AfterContentInit {
674
699
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlCarousel, "ol-carousel", never, { "activeIndex": { "alias": "activeIndex"; "required": false; "isSignal": true; }; "slideCountInput": { "alias": "slideCount"; "required": false; "isSignal": true; }; "indicators": { "alias": "indicators"; "required": false; "isSignal": true; }; "controls": { "alias": "controls"; "required": false; "isSignal": true; }; }, { "activeIndex": "activeIndexChange"; }, never, ["*"], true, never>;
675
700
  }
676
701
 
702
+ /**
703
+ * Một slide trong `ol-carousel`.
704
+ *
705
+ * Component này CHỈ là vỏ ngữ nghĩa — `ol-carousel` dựng track trượt ngang
706
+ * (`translateX` + `[&>*]:w-full [&>*]:shrink-0`) nên bề rộng và vị trí do track
707
+ * quyết định, slide không tự đặt.
708
+ *
709
+ * BẢN CŨ SAI: template là `<div class="absolute inset-0 …">` kèm `opacity-100/0`
710
+ * theo input `active` — kiểu chồng lớp của carousel đời trước. Khi `ol-carousel`
711
+ * chuyển sang track trượt thì hai cơ chế xung khắc. Đo trên /carousel
712
+ * (2026-09-16), chiếu 3 slide vào track:
713
+ *
714
+ * host left 385 · 1439 · 2493 (xếp ngang ĐÚNG)
715
+ * ruột left 385 · 385 · 385 (CHỒNG NHAU)
716
+ *
717
+ * `absolute inset-0` neo theo tổ tiên `position: relative` gần nhất — đó là
718
+ * khung ngoài của carousel, KHÔNG phải host. Nên cả ba nội dung đè lên nhau ở
719
+ * vị trí slide đầu. Vì vậy docs phải chiếu `<div>` thô vào và component này
720
+ * chưa bao giờ được dùng (0 lượt).
721
+ *
722
+ * `host: { class: 'block h-full' }` là BẮT BUỘC: thẻ tuỳ biến mặc định
723
+ * `display: inline`, mà `[&>*]:w-full` của track không ép nổi chiều cao — đo
724
+ * bản thiếu class: host cao 0, slide trống trơn.
725
+ */
677
726
  declare class OlCarouselSlide {
678
- readonly active: _angular_core.InputSignal<boolean>;
679
727
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlCarouselSlide, never>;
680
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlCarouselSlide, "ol-carousel-slide", never, { "active": { "alias": "active"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
728
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlCarouselSlide, "ol-carousel-slide", never, {}, {}, never, ["*"], true, never>;
681
729
  }
682
730
 
683
731
  declare class OlClipboard {
@@ -700,7 +748,7 @@ declare class OlCollapse {
700
748
 
701
749
  type DatepickerViewMode = 'day' | 'month' | 'year';
702
750
  type DatepickerSelectionMode = 'single' | 'range';
703
- type DatepickerSize = 'sm' | 'md' | 'lg';
751
+ type DatepickerSize = 'sm' | 'base' | 'lg';
704
752
  type DateRange = [Date | null, Date | null];
705
753
  interface DateCustomClass {
706
754
  date: Date;
@@ -813,6 +861,19 @@ declare class OlDatepicker implements ControlValueAccessor {
813
861
  readonly daysDisabled: _angular_core.InputSignal<number[]>;
814
862
  readonly datesDisabled: _angular_core.InputSignal<Date[]>;
815
863
  readonly datesEnabled: _angular_core.InputSignal<Date[]>;
864
+ /**
865
+ * Đẩy khung lịch ra `<body>` thay vì để trong cây DOM của component.
866
+ *
867
+ * Cần khi component nằm trong modal / drawer / bảng có `overflow: hidden`:
868
+ * khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING CONTEXT —
869
+ * z-index của lớp nổi không thoát ra được — và `overflow: hidden` CẮT luôn
870
+ * phần tràn. Hai lỗi chồng nhau, z-index một mình không chữa nổi cái thứ
871
+ * hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
872
+ *
873
+ * Mặc định TẮT để không đổi hành vi mã đang chạy. Cùng tên và cùng ý nghĩa
874
+ * với `appendTo` của ng-select và PrimeNG.
875
+ */
876
+ readonly appendTo: _angular_core.InputSignal<"body" | null>;
816
877
  readonly showFooter: _angular_core.InputSignal<boolean>;
817
878
  readonly inline: _angular_core.InputSignal<boolean>;
818
879
  readonly size: _angular_core.InputSignal<DatepickerSize>;
@@ -821,6 +882,12 @@ declare class OlDatepicker implements ControlValueAccessor {
821
882
  readonly timestampChange: _angular_core.OutputEmitterRef<number | null>;
822
883
  readonly isoChange: _angular_core.OutputEmitterRef<string | null>;
823
884
  readonly formattedChange: _angular_core.OutputEmitterRef<string>;
885
+ private readonly panel;
886
+ private readonly overlayContainer;
887
+ private readonly positioning;
888
+ /** Hàm trả panel về chỗ cũ + hàm ngắt theo dõi vị trí. */
889
+ private goPanel;
890
+ private ngatTheoDoi;
824
891
  readonly isOpen: _angular_core.WritableSignal<boolean>;
825
892
  readonly inputValue: _angular_core.WritableSignal<string>;
826
893
  private onChange;
@@ -840,7 +907,7 @@ declare class OlDatepicker implements ControlValueAccessor {
840
907
  * `md`→w-5 khớp đúng ba giá trị bản cũ định dùng, và phần đệm để nguyên cho
841
908
  * div bọc (`ps-3.5`) lo — trộn vào đây là chỗ sinh lệch.
842
909
  */
843
- readonly iconSize: _angular_core.Signal<"xs" | "sm" | "md">;
910
+ readonly iconSize: _angular_core.Signal<"xs" | "sm" | "base">;
844
911
  writeValue(val: Date | null | string): void;
845
912
  registerOnChange(fn: (val: Date | null) => void): void;
846
913
  registerOnTouched(fn: () => void): void;
@@ -851,12 +918,15 @@ declare class OlDatepicker implements ControlValueAccessor {
851
918
  onPopupClear(): void;
852
919
  onInputChange(val: string): void;
853
920
  clearInput(event: MouseEvent): void;
921
+ /** Ngắt theo dõi vị trí và trả panel về chỗ cũ. Gọi được nhiều lần. */
922
+ private donDepPanel;
854
923
  onDocumentClick(event: MouseEvent): void;
855
924
  onEscape(): void;
856
925
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlDatepicker, never>;
857
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDatepicker, "ol-datepicker", never, { "id": { "alias": "id"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "format": { "alias": "format"; "required": false; "isSignal": true; }; "minDate": { "alias": "minDate"; "required": false; "isSignal": true; }; "maxDate": { "alias": "maxDate"; "required": false; "isSignal": true; }; "daysDisabled": { "alias": "daysDisabled"; "required": false; "isSignal": true; }; "datesDisabled": { "alias": "datesDisabled"; "required": false; "isSignal": true; }; "datesEnabled": { "alias": "datesEnabled"; "required": false; "isSignal": true; }; "showFooter": { "alias": "showFooter"; "required": false; "isSignal": true; }; "inline": { "alias": "inline"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; "dateChange": "dateChange"; "timestampChange": "timestampChange"; "isoChange": "isoChange"; "formattedChange": "formattedChange"; }, never, never, true, never>;
926
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDatepicker, "ol-datepicker", never, { "id": { "alias": "id"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "format": { "alias": "format"; "required": false; "isSignal": true; }; "minDate": { "alias": "minDate"; "required": false; "isSignal": true; }; "maxDate": { "alias": "maxDate"; "required": false; "isSignal": true; }; "daysDisabled": { "alias": "daysDisabled"; "required": false; "isSignal": true; }; "datesDisabled": { "alias": "datesDisabled"; "required": false; "isSignal": true; }; "datesEnabled": { "alias": "datesEnabled"; "required": false; "isSignal": true; }; "appendTo": { "alias": "appendTo"; "required": false; "isSignal": true; }; "showFooter": { "alias": "showFooter"; "required": false; "isSignal": true; }; "inline": { "alias": "inline"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; "dateChange": "dateChange"; "timestampChange": "timestampChange"; "isoChange": "isoChange"; "formattedChange": "formattedChange"; }, never, never, true, never>;
858
927
  }
859
928
 
929
+ type OlDateRangePickerVariant = 'dual-inputs' | 'joined';
860
930
  declare class OlDateRangePicker implements ControlValueAccessor {
861
931
  readonly state: OlDatepickerStateService;
862
932
  private readonly hostEl;
@@ -867,7 +937,7 @@ declare class OlDateRangePicker implements ControlValueAccessor {
867
937
  readonly fromLabel: _angular_core.InputSignal<string>;
868
938
  readonly toLabel: _angular_core.InputSignal<string>;
869
939
  readonly showInfoIcon: _angular_core.InputSignal<boolean>;
870
- readonly variant: _angular_core.InputSignal<"dual-inputs" | "joined">;
940
+ readonly variant: _angular_core.InputSignal<OlDateRangePickerVariant>;
871
941
  readonly dualMonth: _angular_core.InputSignal<boolean>;
872
942
  readonly helperText: _angular_core.InputSignal<string>;
873
943
  readonly required: _angular_core.InputSignal<boolean>;
@@ -883,12 +953,31 @@ declare class OlDateRangePicker implements ControlValueAccessor {
883
953
  readonly daysDisabled: _angular_core.InputSignal<number[]>;
884
954
  readonly datesDisabled: _angular_core.InputSignal<Date[]>;
885
955
  readonly datesEnabled: _angular_core.InputSignal<Date[]>;
956
+ /**
957
+ * Đẩy khung lịch ra `<body>` thay vì để trong cây DOM của component.
958
+ *
959
+ * Cần khi component nằm trong modal / drawer / bảng có `overflow: hidden`:
960
+ * khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING CONTEXT —
961
+ * z-index của lớp nổi không thoát ra được — và `overflow: hidden` CẮT luôn
962
+ * phần tràn. Hai lỗi chồng nhau, z-index một mình không chữa nổi cái thứ
963
+ * hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
964
+ *
965
+ * Mặc định TẮT để không đổi hành vi mã đang chạy. Cùng tên và cùng ý nghĩa
966
+ * với `appendTo` của ng-select và PrimeNG.
967
+ */
968
+ readonly appendTo: _angular_core.InputSignal<"body" | null>;
886
969
  readonly showFooter: _angular_core.InputSignal<boolean>;
887
970
  readonly presets: _angular_core.InputSignal<DatepickerPreset[]>;
888
971
  readonly inline: _angular_core.InputSignal<boolean>;
889
972
  readonly size: _angular_core.InputSignal<DatepickerSize>;
890
973
  readonly clearable: _angular_core.InputSignal<boolean>;
891
974
  readonly rangeChange: _angular_core.OutputEmitterRef<DateRange | null>;
975
+ private readonly panel;
976
+ private readonly overlayContainer;
977
+ private readonly positioning;
978
+ /** Hàm trả panel về chỗ cũ + hàm ngắt theo dõi vị trí. */
979
+ private goPanel;
980
+ private ngatTheoDoi;
892
981
  readonly isOpen: _angular_core.WritableSignal<boolean>;
893
982
  readonly startInputValue: _angular_core.WritableSignal<string>;
894
983
  readonly endInputValue: _angular_core.WritableSignal<string>;
@@ -900,7 +989,7 @@ declare class OlDateRangePicker implements ControlValueAccessor {
900
989
  * đầy đủ ở đó. Bốn icon lịch trong template này từng khoá cứng `size="sm"`
901
990
  * trong khi ô nhập co giãn theo `inputSizeClasses()`.
902
991
  */
903
- readonly iconSize: _angular_core.Signal<"xs" | "sm" | "md">;
992
+ readonly iconSize: _angular_core.Signal<"xs" | "sm" | "base">;
904
993
  readonly inputSizeClasses: _angular_core.Signal<"py-2 px-3 text-xs rounded-lg" | "py-3.5 px-4 text-base rounded-md" | "py-2.5 px-3.5 text-sm rounded-md">;
905
994
  writeValue(val: DateRange | null): void;
906
995
  registerOnChange(fn: (val: DateRange | null) => void): void;
@@ -915,10 +1004,12 @@ declare class OlDateRangePicker implements ControlValueAccessor {
915
1004
  onStartInputChange(val: string): void;
916
1005
  onEndInputChange(val: string): void;
917
1006
  clearRange(event: MouseEvent): void;
1007
+ /** Ngắt theo dõi vị trí và trả panel về chỗ cũ. Gọi được nhiều lần. */
1008
+ private donDepPanel;
918
1009
  onDocumentClick(event: MouseEvent): void;
919
1010
  onEscape(): void;
920
1011
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlDateRangePicker, never>;
921
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDateRangePicker, "ol-date-range-picker", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "startPlaceholder": { "alias": "startPlaceholder"; "required": false; "isSignal": true; }; "endPlaceholder": { "alias": "endPlaceholder"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "fromLabel": { "alias": "fromLabel"; "required": false; "isSignal": true; }; "toLabel": { "alias": "toLabel"; "required": false; "isSignal": true; }; "showInfoIcon": { "alias": "showInfoIcon"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "dualMonth": { "alias": "dualMonth"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "format": { "alias": "format"; "required": false; "isSignal": true; }; "minDate": { "alias": "minDate"; "required": false; "isSignal": true; }; "maxDate": { "alias": "maxDate"; "required": false; "isSignal": true; }; "daysDisabled": { "alias": "daysDisabled"; "required": false; "isSignal": true; }; "datesDisabled": { "alias": "datesDisabled"; "required": false; "isSignal": true; }; "datesEnabled": { "alias": "datesEnabled"; "required": false; "isSignal": true; }; "showFooter": { "alias": "showFooter"; "required": false; "isSignal": true; }; "presets": { "alias": "presets"; "required": false; "isSignal": true; }; "inline": { "alias": "inline"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; "rangeChange": "rangeChange"; }, never, never, true, never>;
1012
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDateRangePicker, "ol-date-range-picker", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "startPlaceholder": { "alias": "startPlaceholder"; "required": false; "isSignal": true; }; "endPlaceholder": { "alias": "endPlaceholder"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "fromLabel": { "alias": "fromLabel"; "required": false; "isSignal": true; }; "toLabel": { "alias": "toLabel"; "required": false; "isSignal": true; }; "showInfoIcon": { "alias": "showInfoIcon"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "dualMonth": { "alias": "dualMonth"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "format": { "alias": "format"; "required": false; "isSignal": true; }; "minDate": { "alias": "minDate"; "required": false; "isSignal": true; }; "maxDate": { "alias": "maxDate"; "required": false; "isSignal": true; }; "daysDisabled": { "alias": "daysDisabled"; "required": false; "isSignal": true; }; "datesDisabled": { "alias": "datesDisabled"; "required": false; "isSignal": true; }; "datesEnabled": { "alias": "datesEnabled"; "required": false; "isSignal": true; }; "appendTo": { "alias": "appendTo"; "required": false; "isSignal": true; }; "showFooter": { "alias": "showFooter"; "required": false; "isSignal": true; }; "presets": { "alias": "presets"; "required": false; "isSignal": true; }; "inline": { "alias": "inline"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; "rangeChange": "rangeChange"; }, never, never, true, never>;
922
1013
  }
923
1014
 
924
1015
  declare class OlDatepickerDirective implements OnDestroy {
@@ -1059,7 +1150,7 @@ declare function generateYearsMatrix(viewDate: Date, selectedDate?: Date | null,
1059
1150
  * cạnh dưới. */
1060
1151
  type DrawerPlacement = 'left' | 'right' | 'top' | 'bottom';
1061
1152
  /** Bề rộng (với left/right) hoặc chiều cao (với top/bottom). */
1062
- type DrawerSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';
1153
+ type DrawerSize = 'sm' | 'base' | 'lg' | 'xl' | 'full';
1063
1154
  declare class OlDrawer {
1064
1155
  private readonly focusTrap;
1065
1156
  private readonly destroyRef;
@@ -1071,6 +1162,16 @@ declare class OlDrawer {
1071
1162
  readonly closeOnBackdrop: _angular_core.InputSignal<boolean>;
1072
1163
  readonly closeOnEscape: _angular_core.InputSignal<boolean>;
1073
1164
  readonly showClose: _angular_core.InputSignal<boolean>;
1165
+ /**
1166
+ * `ol-drawer-header` cũng tự vẽ nút đóng và cũng mặc định bật. Dùng chung
1167
+ * hai component là HIỆN HAI CHỮ X chồng nhau — đo DOM /drawer ngày
1168
+ * 2026-09-16 đếm được 2 nút trong cùng một ngăn kéo.
1169
+ *
1170
+ * Có header thì nhường nút đóng cho nó: header đặt nút trong hàng tiêu đề,
1171
+ * đúng chỗ người dùng tìm, còn nút của `ol-drawer` neo tuyệt đối vào góc.
1172
+ */
1173
+ private readonly header;
1174
+ protected readonly hienNutDong: _angular_core.Signal<boolean>;
1074
1175
  /** Nhãn cho trình đọc màn hình khi drawer không có tiêu đề nhìn thấy được. */
1075
1176
  readonly ariaLabel: _angular_core.InputSignal<string>;
1076
1177
  readonly closed: _angular_core.OutputEmitterRef<void>;
@@ -1084,7 +1185,7 @@ declare class OlDrawer {
1084
1185
  toggle(): void;
1085
1186
  onBackdropClick(): void;
1086
1187
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlDrawer, never>;
1087
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDrawer, "ol-drawer", never, { "isOpen": { "alias": "isOpen"; "required": false; "isSignal": true; }; "placement": { "alias": "placement"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "backdrop": { "alias": "backdrop"; "required": false; "isSignal": true; }; "closeOnBackdrop": { "alias": "closeOnBackdrop"; "required": false; "isSignal": true; }; "closeOnEscape": { "alias": "closeOnEscape"; "required": false; "isSignal": true; }; "showClose": { "alias": "showClose"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "isOpen": "isOpenChange"; "closed": "closed"; }, never, ["*"], true, never>;
1188
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDrawer, "ol-drawer", never, { "isOpen": { "alias": "isOpen"; "required": false; "isSignal": true; }; "placement": { "alias": "placement"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "backdrop": { "alias": "backdrop"; "required": false; "isSignal": true; }; "closeOnBackdrop": { "alias": "closeOnBackdrop"; "required": false; "isSignal": true; }; "closeOnEscape": { "alias": "closeOnEscape"; "required": false; "isSignal": true; }; "showClose": { "alias": "showClose"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "isOpen": "isOpenChange"; "closed": "closed"; }, ["header"], ["*"], true, never>;
1088
1189
  }
1089
1190
 
1090
1191
  /**
@@ -1143,9 +1244,25 @@ declare class OlDropdown {
1143
1244
  private readonly menuRef;
1144
1245
  private readonly cursorAnchorRef;
1145
1246
  private hideTimeoutId;
1247
+ private readonly overlayContainer;
1146
1248
  private cleanupAutoUpdate;
1249
+ /** Hàm trả menu về chỗ cũ khi đóng. */
1250
+ private goMenu;
1147
1251
  readonly label: _angular_core.InputSignal<string | null>;
1148
1252
  readonly isOpen: _angular_core.ModelSignal<boolean>;
1253
+ /**
1254
+ * Đẩy menu ra `<body>` thay vì để trong cây DOM của component.
1255
+ *
1256
+ * Cần khi component nằm trong modal / drawer / bảng có `overflow: hidden`:
1257
+ * khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING CONTEXT —
1258
+ * z-index của lớp nổi không thoát ra được — và `overflow: hidden` CẮT luôn
1259
+ * phần tràn. Hai lỗi chồng nhau, z-index một mình không chữa nổi cái thứ
1260
+ * hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
1261
+ *
1262
+ * Mặc định TẮT để không đổi hành vi mã đang chạy. Cùng tên và cùng ý nghĩa
1263
+ * với `appendTo` của ng-select và PrimeNG.
1264
+ */
1265
+ readonly appendTo: _angular_core.InputSignal<"body" | null>;
1149
1266
  readonly placement: _angular_core.InputSignal<DropdownPlacement>;
1150
1267
  readonly trigger: _angular_core.InputSignal<DropdownTrigger>;
1151
1268
  readonly autoClose: _angular_core.InputSignal<DropdownAutoClose>;
@@ -1183,7 +1300,7 @@ declare class OlDropdown {
1183
1300
  toggle(): void;
1184
1301
  private clearHideTimeout;
1185
1302
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlDropdown, never>;
1186
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDropdown, "ol-dropdown", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "isOpen": { "alias": "isOpen"; "required": false; "isSignal": true; }; "placement": { "alias": "placement"; "required": false; "isSignal": true; }; "trigger": { "alias": "trigger"; "required": false; "isSignal": true; }; "autoClose": { "alias": "autoClose"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "triggerClass": { "alias": "triggerClass"; "required": false; "isSignal": true; }; "menuClass": { "alias": "menuClass"; "required": false; "isSignal": true; }; }, { "isOpen": "isOpenChange"; "opened": "opened"; "closed": "closed"; }, never, ["[slot=trigger], [trigger]", "[triggerIcon]", "[triggerIcon]", "*"], true, never>;
1303
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDropdown, "ol-dropdown", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "isOpen": { "alias": "isOpen"; "required": false; "isSignal": true; }; "appendTo": { "alias": "appendTo"; "required": false; "isSignal": true; }; "placement": { "alias": "placement"; "required": false; "isSignal": true; }; "trigger": { "alias": "trigger"; "required": false; "isSignal": true; }; "autoClose": { "alias": "autoClose"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "triggerClass": { "alias": "triggerClass"; "required": false; "isSignal": true; }; "menuClass": { "alias": "menuClass"; "required": false; "isSignal": true; }; }, { "isOpen": "isOpenChange"; "opened": "opened"; "closed": "closed"; }, never, ["[slot=trigger], [trigger]", "[triggerIcon]", "[triggerIcon]", "*"], true, never>;
1187
1304
  }
1188
1305
 
1189
1306
  declare class OlDropdownItem {
@@ -1339,8 +1456,9 @@ interface HelperFeature {
1339
1456
  * Icon: Figma dùng `close` cho điều kiện chưa đạt, nhưng registry KHÔNG có tên
1340
1457
  * đó (cả ba bộ đều 0, aliasMap cũng không) — dùng `close-circle` thay.
1341
1458
  */
1459
+ type OlHelperType = 'default' | 'action' | 'strength' | 'tags' | 'features';
1342
1460
  declare class OlHelper {
1343
- readonly type: _angular_core.InputSignal<"default" | "action" | "strength" | "tags" | "features">;
1461
+ readonly type: _angular_core.InputSignal<OlHelperType>;
1344
1462
  /** Chữ gợi ý chính — dùng ở `default`, `action`. */
1345
1463
  readonly text: _angular_core.InputSignal<string>;
1346
1464
  readonly state: _angular_core.InputSignal<HelperState>;
@@ -1426,7 +1544,8 @@ type ChoiceShape = 'card' | 'chip' | 'bare' | 'pill';
1426
1544
  * `ol-toggle` mà `ol-toggle` tự dựng thẻ `<label>` — lồng hai `<label>` là HTML
1427
1545
  * hỏng. Bốn kiểu này để lô sau.
1428
1546
  */
1429
- declare class OlCheckboxRadioForm {
1547
+ type OlCheckboxRadioFormAlign = 'start' | 'center';
1548
+ declare class OlCheckboxRadioForm implements ControlValueAccessor {
1430
1549
  readonly checked: _angular_core.ModelSignal<boolean>;
1431
1550
  readonly disabled: _angular_core.ModelSignal<boolean>;
1432
1551
  readonly control: _angular_core.InputSignal<ChoiceControl>;
@@ -1434,7 +1553,7 @@ declare class OlCheckboxRadioForm {
1434
1553
  readonly layout: _angular_core.InputSignal<ChoiceLayout>;
1435
1554
  readonly shape: _angular_core.InputSignal<ChoiceShape>;
1436
1555
  /** Figma canh giữa ở `Icons & text` và `Payment`, canh đầu ở các kiểu còn lại. */
1437
- readonly align: _angular_core.InputSignal<"center" | "start">;
1556
+ readonly align: _angular_core.InputSignal<OlCheckboxRadioFormAlign>;
1438
1557
  readonly label: _angular_core.InputSignal<string>;
1439
1558
  readonly helper: _angular_core.InputSignal<string>;
1440
1559
  /** Con số cuối nhãn — Figma `ol-label-choice` variant `With number`. */
@@ -1461,6 +1580,20 @@ declare class OlCheckboxRadioForm {
1461
1580
  /** Ô vuông của `checkbox`, ô tròn của `radio` — Figma đều 16px. */
1462
1581
  readonly boxClasses: _angular_core.Signal<string>;
1463
1582
  toggle(): void;
1583
+ /**
1584
+ * Thiếu bốn phương thức này thì `formControlName` / `[formControl]` KHÔNG
1585
+ * gắn được — dùng trong Reactive Forms là lỗi runtime "No value accessor
1586
+ * for form control".
1587
+ */
1588
+ private onChange;
1589
+ private onTouched;
1590
+ /** Lần chạy đầu của `effect` là lúc dựng, chưa phải người dùng đổi. */
1591
+ private daKhoiTao;
1592
+ constructor();
1593
+ writeValue(val: boolean): void;
1594
+ registerOnChange(fn: (val: boolean) => void): void;
1595
+ registerOnTouched(fn: () => void): void;
1596
+ setDisabledState(isDisabled: boolean): void;
1464
1597
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlCheckboxRadioForm, never>;
1465
1598
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlCheckboxRadioForm, "ol-checkbox-radio-form", never, { "checked": { "alias": "checked"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "control": { "alias": "control"; "required": false; "isSignal": true; }; "controlPosition": { "alias": "controlPosition"; "required": false; "isSignal": true; }; "layout": { "alias": "layout"; "required": false; "isSignal": true; }; "shape": { "alias": "shape"; "required": false; "isSignal": true; }; "align": { "alias": "align"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "helper": { "alias": "helper"; "required": false; "isSignal": true; }; "number": { "alias": "number"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "iconBoxed": { "alias": "iconBoxed"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "inputId": { "alias": "inputId"; "required": false; "isSignal": true; }; }, { "checked": "checkedChange"; "disabled": "disabledChange"; "changed": "changed"; }, never, ["[lead]", "*"], true, never>;
1466
1599
  }
@@ -1486,6 +1619,7 @@ type ChoiceGroupLayout = 'stack' | 'row' | 'grid';
1486
1619
  * Nên ở đây chỉ giữ đúng phần component hoá được: hướng xếp, số cột, khoảng
1487
1620
  * cách 16px, và vai trò nhóm mà bộ đọc màn hình cần.
1488
1621
  */
1622
+ type OlCheckboxRadioGroupRole = 'group' | 'radiogroup';
1489
1623
  declare class OlCheckboxRadioGroup {
1490
1624
  /** Tiêu đề nhóm. Rỗng thì không render, nhưng nhóm vẫn giữ vai trò. */
1491
1625
  readonly label: _angular_core.InputSignal<string>;
@@ -1493,7 +1627,7 @@ declare class OlCheckboxRadioGroup {
1493
1627
  /** Số cột khi `layout='grid'`. Figma `Big cards grid` để 2 cột. */
1494
1628
  readonly columns: _angular_core.InputSignal<number>;
1495
1629
  /** `radiogroup` khi các thẻ bên trong chỉ chọn được một. */
1496
- readonly role: _angular_core.InputSignal<"group" | "radiogroup">;
1630
+ readonly role: _angular_core.InputSignal<OlCheckboxRadioGroupRole>;
1497
1631
  /**
1498
1632
  * Tên class phải viết NGUYÊN VĂN trong mã. Tailwind v4 quét mã nguồn dạng
1499
1633
  * chữ để sinh CSS, nên `grid-cols-${n}` ghép lúc chạy sẽ không có luật nào
@@ -1513,7 +1647,7 @@ type InputSize = 'sm' | 'base' | 'lg' | 'xl';
1513
1647
  * có nhánh nào, nên `state="warning"` render y hệt `default`. Đã xoá.
1514
1648
  */
1515
1649
  type InputState = 'default' | 'error' | 'success';
1516
- declare class OlInput {
1650
+ declare class OlInput implements ControlValueAccessor {
1517
1651
  /** Id tự sinh, dùng khi nơi gọi không truyền id — để <label for> và
1518
1652
  * aria-describedby luôn nối được với ô nhập. */
1519
1653
  private readonly uid;
@@ -1522,7 +1656,12 @@ declare class OlInput {
1522
1656
  readonly label: _angular_core.InputSignal<string>;
1523
1657
  readonly placeholder: _angular_core.InputSignal<string>;
1524
1658
  readonly helperText: _angular_core.InputSignal<string>;
1525
- readonly disabled: _angular_core.InputSignal<boolean>;
1659
+ /**
1660
+ * `model()` chứ không `input()`: Reactive Forms gọi `setDisabledState()` khi
1661
+ * form khoá/mở control, mà `input()` chỉ đọc nên không ghi được. Cách dùng
1662
+ * từ ngoài KHÔNG đổi — `[disabled]="true"` và `disabled()` vẫn như cũ.
1663
+ */
1664
+ readonly disabled: _angular_core.ModelSignal<boolean>;
1526
1665
  readonly readonly: _angular_core.InputSignal<boolean>;
1527
1666
  readonly size: _angular_core.InputSignal<InputSize>;
1528
1667
  readonly state: _angular_core.InputSignal<InputState>;
@@ -1553,12 +1692,27 @@ declare class OlInput {
1553
1692
  * lệch 2px so với thiết kế.
1554
1693
  */
1555
1694
  readonly paddingClasses: _angular_core.Signal<string>;
1695
+ /**
1696
+ * Bốn phương thức này là thứ Angular Forms gọi tới. Thiếu chúng thì
1697
+ * `formControlName` / `[formControl]` KHÔNG gắn được — dùng component trong
1698
+ * Reactive Forms là lỗi runtime "No value accessor for form control".
1699
+ */
1700
+ private onChange;
1701
+ private onTouched;
1702
+ writeValue(val: string): void;
1703
+ registerOnChange(fn: (val: string) => void): void;
1704
+ registerOnTouched(fn: () => void): void;
1705
+ setDisabledState(isDisabled: boolean): void;
1706
+ /** Gọi từ template mỗi khi giá trị đổi, để form biết. */
1707
+ protected phatThayDoi(val: string): void;
1708
+ /** Gọi khi rời ô nhập — form dùng để đánh dấu `touched`. */
1709
+ protected danhDauCham(): void;
1556
1710
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlInput, never>;
1557
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlInput, "ol-input", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "showAsterisk": { "alias": "showAsterisk"; "required": false; "isSignal": true; }; "labelIcon": { "alias": "labelIcon"; "required": false; "isSignal": true; }; "labelTooltip": { "alias": "labelTooltip"; "required": false; "isSignal": true; }; "prefixText": { "alias": "prefixText"; "required": false; "isSignal": true; }; "suffixText": { "alias": "suffixText"; "required": false; "isSignal": true; }; "addOnIcon": { "alias": "addOnIcon"; "required": false; "isSignal": true; }; "leftIcon": { "alias": "leftIcon"; "required": false; "isSignal": true; }; "rightIcon": { "alias": "rightIcon"; "required": false; "isSignal": true; }; "rightHelperText": { "alias": "rightHelperText"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; }, never, ["[addOnIcon], [leftIcon]", "[leftIcon]", "[rightIcon]", "[rightHelperAction]"], true, never>;
1711
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlInput, "ol-input", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "showAsterisk": { "alias": "showAsterisk"; "required": false; "isSignal": true; }; "labelIcon": { "alias": "labelIcon"; "required": false; "isSignal": true; }; "labelTooltip": { "alias": "labelTooltip"; "required": false; "isSignal": true; }; "prefixText": { "alias": "prefixText"; "required": false; "isSignal": true; }; "suffixText": { "alias": "suffixText"; "required": false; "isSignal": true; }; "addOnIcon": { "alias": "addOnIcon"; "required": false; "isSignal": true; }; "leftIcon": { "alias": "leftIcon"; "required": false; "isSignal": true; }; "rightIcon": { "alias": "rightIcon"; "required": false; "isSignal": true; }; "rightHelperText": { "alias": "rightHelperText"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; }, never, ["[addOnIcon], [leftIcon]", "[leftIcon]", "[rightIcon]", "[rightHelperAction]"], true, never>;
1558
1712
  }
1559
1713
 
1560
1714
  type TextareaState = 'default' | 'error' | 'success';
1561
- declare class OlTextarea {
1715
+ declare class OlTextarea implements ControlValueAccessor {
1562
1716
  /** Id tự sinh khi nơi gọi không truyền id — để <label for> và
1563
1717
  * aria-describedby luôn nối được với ô nhập. */
1564
1718
  private readonly uid;
@@ -1570,7 +1724,12 @@ declare class OlTextarea {
1570
1724
  readonly label: _angular_core.InputSignal<string>;
1571
1725
  readonly placeholder: _angular_core.InputSignal<string>;
1572
1726
  readonly rows: _angular_core.InputSignal<number>;
1573
- readonly disabled: _angular_core.InputSignal<boolean>;
1727
+ /**
1728
+ * `model()` chứ không `input()`: Reactive Forms gọi `setDisabledState()` khi
1729
+ * form khoá/mở control, mà `input()` chỉ đọc nên không ghi được. Cách dùng
1730
+ * từ ngoài KHÔNG đổi — `[disabled]="true"` và `disabled()` vẫn như cũ.
1731
+ */
1732
+ readonly disabled: _angular_core.ModelSignal<boolean>;
1574
1733
  readonly helperText: _angular_core.InputSignal<string>;
1575
1734
  readonly maxLength: _angular_core.InputSignal<number | undefined>;
1576
1735
  readonly showCharCount: _angular_core.InputSignal<boolean>;
@@ -1581,47 +1740,425 @@ declare class OlTextarea {
1581
1740
  readonly id: _angular_core.InputSignal<string>;
1582
1741
  readonly submitComment: _angular_core.OutputEmitterRef<string>;
1583
1742
  onSubmit(): void;
1743
+ /**
1744
+ * Bốn phương thức này là thứ Angular Forms gọi tới. Thiếu chúng thì
1745
+ * `formControlName` / `[formControl]` KHÔNG gắn được — dùng component trong
1746
+ * Reactive Forms là lỗi runtime "No value accessor for form control".
1747
+ */
1748
+ private onChange;
1749
+ private onTouched;
1750
+ writeValue(val: string): void;
1751
+ registerOnChange(fn: (val: string) => void): void;
1752
+ registerOnTouched(fn: () => void): void;
1753
+ setDisabledState(isDisabled: boolean): void;
1754
+ /** Gọi từ template mỗi khi giá trị đổi, để form biết. */
1755
+ protected phatThayDoi(val: string): void;
1756
+ /** Gọi khi rời ô nhập — form dùng để đánh dấu `touched`. */
1757
+ protected danhDauCham(): void;
1584
1758
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlTextarea, never>;
1585
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTextarea, "ol-textarea", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "rows": { "alias": "rows"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "maxLength": { "alias": "maxLength"; "required": false; "isSignal": true; }; "showCharCount": { "alias": "showCharCount"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "showAsterisk": { "alias": "showAsterisk"; "required": false; "isSignal": true; }; "commentBox": { "alias": "commentBox"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "submitComment": "submitComment"; }, never, never, true, never>;
1759
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTextarea, "ol-textarea", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "rows": { "alias": "rows"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "maxLength": { "alias": "maxLength"; "required": false; "isSignal": true; }; "showCharCount": { "alias": "showCharCount"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "showAsterisk": { "alias": "showAsterisk"; "required": false; "isSignal": true; }; "commentBox": { "alias": "commentBox"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; "submitComment": "submitComment"; }, never, never, true, never>;
1586
1760
  }
1587
1761
 
1588
- type SelectSize = 'sm' | 'md' | 'lg';
1762
+ /**
1763
+ * Thang cỡ theo Figma: `sm | base | lg`. `'md'` là BÍ DANH của `'base'`,
1764
+ * giữ cho mã cũ chạy tiếp và sẽ bỏ ở bản sau — dùng `'base'` cho mã mới.
1765
+ */
1766
+ type SelectSize = 'sm' | 'base' | 'lg' | 'md';
1589
1767
  type SelectState = 'default' | 'error' | 'success';
1590
1768
  interface SelectOption {
1591
1769
  label: string;
1592
1770
  value: any;
1593
1771
  disabled?: boolean;
1772
+ /**
1773
+ * Chip của mục này KHÔNG có nút gỡ, và Backspace cũng không gỡ được.
1774
+ *
1775
+ * Khác `disabled`: `disabled` chặn CHỌN mục, `locked` cho chọn nhưng không
1776
+ * cho bỏ. Dùng cho thành viên bắt buộc, nhãn hệ thống, phân loại chỉ đọc.
1777
+ * Nơi gọi vẫn gỡ được bằng cách ghi thẳng vào `value` — đây là ràng buộc ở
1778
+ * giao diện, không phải ở dữ liệu.
1779
+ */
1780
+ locked?: boolean;
1781
+ /**
1782
+ * Icon đứng đầu mục, hiện CẢ trong danh sách LẪN trên ô đóng khi mục này
1783
+ * được chọn. Tên icon theo `OlIconService` (`'star'` hoặc `'lucide:star'`).
1784
+ */
1785
+ icon?: string;
1786
+ /**
1787
+ * Chữ đứng đầu mục — dùng thay `icon` khi phần đầu là văn bản: mã lớp
1788
+ * (`'12A1'`), chữ cái đầu, emoji. Bỏ qua nếu đã có `icon`.
1789
+ */
1790
+ prefix?: string;
1791
+ /**
1792
+ * Class màu cho phần đầu, ví dụ `'text-fg-success'`. Bỏ trống thì phần đầu
1793
+ * theo màu chữ của dòng.
1794
+ *
1795
+ * Nhận CLASS chứ không nhận mã màu: mã màu cứng không đổi theo theme và
1796
+ * không đổi theo chế độ tối — đúng lớp lỗi đã sửa ở `dark:text-white`.
1797
+ */
1798
+ prefixClass?: string;
1799
+ [key: string]: any;
1594
1800
  }
1595
- declare class OlSelect {
1596
- /** Id tự sinh khi nơi gọi không truyền id — để <label for> và
1597
- * aria-describedby luôn nối được với ô nhập. */
1598
- private readonly uid;
1599
- readonly inputId: _angular_core.Signal<string>;
1600
- readonly helperId: _angular_core.Signal<string>;
1601
- readonly describedBy: _angular_core.Signal<string | null>;
1602
- readonly ariaInvalid: _angular_core.Signal<"true" | null>;
1801
+ /** Một dòng trong danh sách đã dựng phẳng — tiêu đề nhóm hoặc mục chọn được. */
1802
+ interface Dong {
1803
+ loai: 'nhom' | 'muc';
1804
+ ten: string;
1805
+ item?: any;
1806
+ /** Chỉ số trong danh sách MỤC (bỏ qua tiêu đề) — dùng cho bàn phím và aria. */
1807
+ chiSo: number;
1808
+ /** Chỉ ở dòng `nhom`: đây có phải nhóm ĐẦU TIÊN không (để không kẻ đường ngăn thừa). */
1809
+ dauTien?: boolean;
1810
+ }
1811
+ /**
1812
+ * `ol-select` — ô chọn có TÌM KIẾM, ĐA CHỌN, GOM NHÓM và CUỘN ẢO.
1813
+ *
1814
+ * Bản trước là thẻ `<select>` HTML gốc bọc khung Figma: 11 input, 0 output,
1815
+ * không tìm kiếm, không đa chọn. Bản này dựng danh sách riêng nên làm được
1816
+ * những việc `<select>` gốc không cho phép.
1817
+ *
1818
+ * ĐỌC `ng-select` 24.1.2 ĐỂ HỌC CÁCH LÀM, KHÔNG CHÉP MÃ và KHÔNG CÀI NÓ:
1819
+ *
1820
+ * · Cuộn ảo — nó tính `indexByScrollTop = scrollTop / itemHeight`, lấy
1821
+ * `end = ceil(index) + itemsPerViewport + 1`, rồi chèn đệm trên/dưới cho
1822
+ * thanh cuộn đúng độ dài. Ở đây làm y hệt nguyên lý ấy bằng `computed`,
1823
+ * không cần `@angular/cdk`.
1824
+ * · Vai trò ARIA — `role="listbox"` cho danh sách, `role="option"` cho mục,
1825
+ * `aria-activedescendant` trỏ mục đang đánh dấu.
1826
+ * · Gỡ nảy cho ô tìm — gộp nhiều phím thành một lượt gọi.
1827
+ *
1828
+ * KHÁC ng-select ở hai chỗ, cố ý:
1829
+ * · nó nhận `typeahead` là một `Subject` để nơi gọi tự nối toán tử RxJS;
1830
+ * ở đây là `output` + `debounceMs` sẵn, nơi gọi chỉ cần một hàm.
1831
+ * · nó là ô chọn trần, phần nhãn/lỗi người dùng tự dựng; ở đây `label`,
1832
+ * `helperText`, `state`, `required` nằm sẵn trong component theo Figma.
1833
+ */
1834
+ declare class OlSelect implements ControlValueAccessor {
1603
1835
  readonly value: _angular_core.ModelSignal<any>;
1604
1836
  readonly options: _angular_core.InputSignal<SelectOption[]>;
1605
1837
  readonly label: _angular_core.InputSignal<string>;
1606
- readonly disabled: _angular_core.InputSignal<boolean>;
1607
1838
  readonly size: _angular_core.InputSignal<SelectSize>;
1608
1839
  readonly helperText: _angular_core.InputSignal<string>;
1609
1840
  readonly state: _angular_core.InputSignal<SelectState>;
1610
1841
  readonly required: _angular_core.InputSignal<boolean>;
1611
1842
  readonly showAsterisk: _angular_core.InputSignal<boolean>;
1612
1843
  readonly leftIcon: _angular_core.InputSignal<boolean>;
1844
+ readonly id: _angular_core.InputSignal<string>;
1845
+ /**
1846
+ * `model()` chứ không `input()`: Reactive Forms gọi `setDisabledState()`,
1847
+ * cần `set()` được. Cách dùng từ ngoài không đổi.
1848
+ */
1849
+ readonly disabled: _angular_core.ModelSignal<boolean>;
1850
+ readonly placeholder: _angular_core.InputSignal<string>;
1851
+ readonly multiple: _angular_core.InputSignal<boolean>;
1852
+ readonly searchable: _angular_core.InputSignal<boolean>;
1853
+ readonly clearable: _angular_core.InputSignal<boolean>;
1854
+ readonly loading: _angular_core.InputSignal<boolean>;
1855
+ readonly readonly: _angular_core.InputSignal<boolean>;
1856
+ readonly labelKey: _angular_core.InputSignal<string>;
1857
+ readonly valueKey: _angular_core.InputSignal<string>;
1858
+ readonly groupBy: _angular_core.InputSignal<string>;
1859
+ readonly emptyText: _angular_core.InputSignal<string>;
1860
+ readonly searchPlaceholder: _angular_core.InputSignal<string>;
1861
+ /**
1862
+ * Đẩy danh sách ra `<body>` thay vì để trong cây DOM của component.
1863
+ *
1864
+ * Cần khi ô chọn nằm trong modal / drawer / bảng có `overflow: hidden`:
1865
+ * đo /modal ngày 2026-09-17 thấy khung modal có `z-index: 80` +
1866
+ * `backdrop-filter` nên TẠO STACKING CONTEXT — `z-dropdown: 50` không thoát
1867
+ * ra được — và hộp thoại `overflow: hidden` CẮT luôn phần tràn. Hai lỗi
1868
+ * chồng nhau, z-index một mình không chữa nổi cái thứ hai.
1869
+ *
1870
+ * Mặc định TẮT để không đổi hành vi mã đang chạy. Bật khi gặp cảnh trên.
1871
+ * Cùng tên và cùng ý nghĩa với `appendTo` của ng-select và PrimeNG.
1872
+ */
1873
+ readonly appendTo: _angular_core.InputSignal<"body" | null>;
1874
+ /** Tối đa bao nhiêu mục được chọn ở chế độ đa chọn. 0 = không giới hạn. */
1875
+ readonly maxSelectedItems: _angular_core.InputSignal<number>;
1876
+ /** Đóng danh sách ngay sau khi chọn. Mặc định: đơn chọn đóng, đa chọn không. */
1877
+ readonly closeOnSelect: _angular_core.InputSignal<boolean | null>;
1878
+ /** Ẩn mục đã chọn khỏi danh sách (chỉ có nghĩa ở chế độ đa chọn). */
1879
+ readonly hideSelected: _angular_core.InputSignal<boolean>;
1880
+ /**
1881
+ * Cho tạo giá trị chưa có trong `options`: gõ rồi bấm nút thêm, hoặc Enter
1882
+ * khi không có mục nào đang được chọn bằng bàn phím.
1883
+ *
1884
+ * Cùng tên và cùng ý nghĩa với `allowCreate` của `ol-autocomplete` — đừng
1885
+ * đặt tên khác cho cùng một khái niệm.
1886
+ */
1887
+ readonly allowCreate: _angular_core.InputSignal<boolean>;
1888
+ /** Mẫu chữ trên nút tạo mới; `{term}` thay bằng chữ người dùng đang gõ. */
1889
+ readonly createText: _angular_core.InputSignal<string>;
1890
+ /** Kẻ một đường ngăn giữa các nhóm (chỉ có nghĩa khi `groupBy` bật). */
1891
+ readonly groupSeparator: _angular_core.InputSignal<boolean>;
1892
+ /**
1893
+ * Ô tìm nằm ở đâu khi danh sách mở.
1894
+ *
1895
+ * `'inline'` — thay chỗ nhãn ngay trên ô đóng. MẶC ĐỊNH, giữ nguyên hành
1896
+ * vi từ trước và cùng lối với `ol-autocomplete`.
1897
+ * `'panel'` — nằm ở đầu panel, ô đóng vẫn hiện nhãn đang chọn. Cùng lối
1898
+ * với `ol-command` và với combobox của reui.
1899
+ *
1900
+ * Để mặc định `'inline'` vì 11 nơi trong repo đang dùng lối đó; đổi mặc
1901
+ * định là đổi hành vi của cả 11 chỗ mà không ai yêu cầu.
1902
+ */
1903
+ readonly searchPosition: _angular_core.InputSignal<"panel" | "inline">;
1904
+ /**
1905
+ * Icon đứng đầu ô tìm khi `searchPosition="panel"`.
1906
+ *
1907
+ * Mặc định `'magnifer'` — cùng icon `ol-command` và `ol-autocomplete` dùng,
1908
+ * và Figma `ol-autocomplete-examples` (#1149:53126) cho thấy **6/6 Type đều
1909
+ * có `search` trong ô nhập**, nên có icon là đúng thiết kế chứ không phải
1910
+ * lựa chọn tuỳ ý.
1911
+ *
1912
+ * KHÔNG ăn theo `icon` của mục đang chọn: đây là icon của Ô TÌM (việc tìm),
1913
+ * còn icon mục nói về chính mục đó — hai nghĩa khác nhau. Đặt `''` để bỏ
1914
+ * hẳn icon.
1915
+ */
1916
+ readonly searchIcon: _angular_core.InputSignal<string>;
1917
+ /**
1918
+ * Chữ đứng đầu ô tìm, dùng thay `searchIcon` khi muốn nhãn văn bản
1919
+ * (`'Lọc:'`, `'Tìm'`). Bỏ qua nếu `searchIcon` còn giá trị.
1920
+ */
1921
+ readonly searchPrefix: _angular_core.InputSignal<string>;
1922
+ /**
1923
+ * Icon CỐ ĐỊNH của ô đóng — luôn hiện, dù đã chọn hay chưa.
1924
+ *
1925
+ * KHÁC `icon` của mục: cái đó đổi theo mục đang chọn, cái này đứng yên.
1926
+ * Dùng khi ô chọn có một nghĩa cố định — `🗓 Ngày thi`, `👤 Người phụ
1927
+ * trách` — và chỉ phần GIÁ TRỊ bên phải đổi. Đây là lối reui dùng cho
1928
+ * `Set start` · `Add label` · `No assignee`.
1929
+ *
1930
+ * Khai cả `triggerIcon` và `icon` của mục thì `triggerIcon` thắng: nhãn cố
1931
+ * định không được nhảy theo mục, nếu không nó hết cố định.
1932
+ */
1933
+ readonly triggerIcon: _angular_core.InputSignal<string>;
1934
+ /**
1935
+ * Nhãn CỐ ĐỊNH đứng trước giá trị trong ô đóng — luôn hiện như
1936
+ * `triggerIcon`. Đặt tên qua input, người dùng cuối không sửa.
1937
+ */
1938
+ readonly triggerLabel: _angular_core.InputSignal<string>;
1939
+ /** Bật thì component NGỪNG tự lọc, chỉ phát `search`. */
1940
+ readonly remoteSearch: _angular_core.InputSignal<boolean>;
1941
+ readonly minTermLength: _angular_core.InputSignal<number>;
1942
+ readonly debounceMs: _angular_core.InputSignal<number>;
1943
+ readonly typeToSearchText: _angular_core.InputSignal<string>;
1944
+ /**
1945
+ * Chỉ vẽ những mục trong tầm nhìn. Danh sách vài nghìn trường/học sinh mà
1946
+ * vẽ hết thì trình duyệt đơ.
1947
+ *
1948
+ * Nguyên lý học từ `ng-select`: biết `itemHeight` cố định thì suy ra được
1949
+ * mục đầu từ `scrollTop`, rồi chèn đệm trên/dưới cho thanh cuộn đúng độ
1950
+ * dài. Không cần `@angular/cdk`.
1951
+ */
1952
+ readonly virtualScroll: _angular_core.InputSignal<boolean>;
1953
+ /** Chiều cao MỘT dòng (px). Phải khớp thật, sai là nhảy chỗ khi cuộn. */
1954
+ readonly itemHeight: _angular_core.InputSignal<number>;
1955
+ /** Vẽ dư bao nhiêu dòng trên/dưới tầm nhìn cho đỡ giật. */
1956
+ readonly bufferAmount: _angular_core.InputSignal<number>;
1957
+ readonly search: _angular_core.OutputEmitterRef<string>;
1958
+ readonly opened: _angular_core.OutputEmitterRef<void>;
1959
+ readonly closed: _angular_core.OutputEmitterRef<void>;
1960
+ readonly cleared: _angular_core.OutputEmitterRef<void>;
1961
+ readonly added: _angular_core.OutputEmitterRef<any>;
1962
+ readonly removed: _angular_core.OutputEmitterRef<any>;
1963
+ readonly selectionChange: _angular_core.OutputEmitterRef<any>;
1964
+ optionTemplate?: TemplateRef<any>;
1965
+ labelTemplate?: TemplateRef<any>;
1966
+ /**
1967
+ * HỘP BỌC của lớp nổi — `appendTo="body"` đẩy chính phần tử này ra `<body>`,
1968
+ * nên nó phải chứa CẢ ô tìm (khi `searchPosition="panel"`) lẫn danh sách.
1969
+ * Trỏ vào riêng listbox thì hai phần tách nhau khi đẩy ra body.
1970
+ */
1971
+ private readonly panel;
1972
+ /** Vùng CUỘN — là thẻ `role="listbox"`, không phải hộp bọc. */
1973
+ private readonly danhSach;
1974
+ /** Ô tìm ở đầu panel, để chuyển focus vào khi mở. */
1975
+ private readonly oTimPanel;
1976
+ readonly isOpen: _angular_core.WritableSignal<boolean>;
1977
+ readonly searchQuery: _angular_core.WritableSignal<string>;
1978
+ readonly activeIndex: _angular_core.WritableSignal<number>;
1979
+ readonly scrollTop: _angular_core.WritableSignal<number>;
1980
+ readonly panelHeight: _angular_core.WritableSignal<number>;
1981
+ private readonly uid;
1982
+ readonly inputId: _angular_core.Signal<string>;
1983
+ readonly helperId: _angular_core.Signal<string>;
1984
+ readonly listboxId: _angular_core.Signal<string>;
1985
+ readonly describedBy: _angular_core.Signal<string | null>;
1986
+ readonly ariaInvalid: _angular_core.Signal<"true" | null>;
1987
+ protected readonly resolvedSize: _angular_core.Signal<SelectSize>;
1613
1988
  /**
1614
1989
  * Padding trái: 10 khi có icon, 3.5 khi không.
1615
- * Không dùng [class.ps-3.5] trên template vì Angular cắt phần sau dấu
1616
- * chấm — nó render ra "ps-3" (12px thay vì 14px).
1990
+ * Không dùng `[class.ps-3.5]` vì Angular cắt phần sau dấu chấm — nó render
1991
+ * ra `ps-3` (12px thay vì 14px).
1617
1992
  */
1618
1993
  readonly paddingStartClass: _angular_core.Signal<"ps-10" | "ps-3.5">;
1619
- readonly id: _angular_core.InputSignal<string>;
1994
+ private onChange;
1995
+ private onTouched;
1996
+ private readonly destroyRef;
1997
+ private readonly elementRef;
1998
+ private readonly overlayContainer;
1999
+ private readonly positioning;
2000
+ /** Hàm trả panel về chỗ cũ + hàm ngắt theo dõi vị trí. */
2001
+ private goPanel;
2002
+ private ngatTheoDoi;
2003
+ private daMo;
2004
+ private hengio;
2005
+ constructor();
2006
+ /** Ngắt theo dõi vị trí và trả panel về chỗ cũ. Gọi được nhiều lần. */
2007
+ private donDepPanel;
2008
+ /**
2009
+ * `[minTermLength]="undefined"` (bind vào khoá state chưa có) đè mất giá
2010
+ * trị mặc định, và `x < undefined` ra `NaN` — luôn `false`. Cùng lớp lỗi
2011
+ * BUG-009, nên mọi input số đều đi qua một hàm lọc.
2012
+ */
2013
+ private soDuong;
2014
+ private readonly safeMinTerm;
2015
+ private readonly safeDebounce;
2016
+ private readonly safeItemHeight;
2017
+ private readonly safeBuffer;
2018
+ readonly safeMaxSelected: _angular_core.Signal<number>;
2019
+ getItemLabel(item: any): string;
2020
+ getItemValue(item: any): any;
2021
+ private bangNhau;
2022
+ /**
2023
+ * Giá trị đang chọn, LUÔN trả mảng — template chỉ có một đường đọc.
2024
+ *
2025
+ * CHUỖI RỖNG là CHƯA CHỌN, không phải "đã chọn giá trị rỗng".
2026
+ *
2027
+ * Bản cũ chỉ loại `null`/`undefined`, nên `signal<string>('')` — cách khởi
2028
+ * tạo phổ biến nhất — làm `hasValue()` trả `true`: ô đóng đi vào nhánh "đã
2029
+ * có giá trị" rồi vẽ `<span class="truncate"></span>` RỖNG thay vì
2030
+ * placeholder. Đo trên `/select` ngày 2026-09-18: **6/26 ô chọn** trống
2031
+ * trơn, không chữ không placeholder.
2032
+ *
2033
+ * `0` và `false` thì KHÔNG loại — chúng là giá trị chọn được hợp lệ (mã
2034
+ * lớp 0, cờ bật/tắt). Chỉ `''` mới mơ hồ, và quy ước của các ô nhập là
2035
+ * chuỗi rỗng nghĩa là trống.
2036
+ */
2037
+ readonly selectedValues: _angular_core.Signal<any[]>;
2038
+ isSelected(item: any): boolean;
2039
+ /** Nhãn hiển thị cho một giá trị — tra ngược trong `options`. */
2040
+ getDisplayLabel(val: any): string;
2041
+ /** Nhãn của ô khi đơn chọn: có giá trị thì hiện nhãn, không thì để trống. */
2042
+ readonly singleLabel: _angular_core.Signal<string>;
2043
+ readonly hasValue: _angular_core.Signal<boolean>;
2044
+ /**
2045
+ * Mục dữ liệu đang chọn ở chế độ ĐƠN chọn — để ô đóng vẽ được phần đầu
2046
+ * (`icon`/`prefix`) chứ không chỉ có chữ.
2047
+ *
2048
+ * Trả `null` khi đa chọn: ở đó mỗi giá trị đã thành một chip riêng, phần
2049
+ * đầu vẽ trên từng chip chứ không vẽ một lần cho cả ô.
2050
+ */
2051
+ readonly selectedItem: _angular_core.Signal<SelectOption | null>;
2052
+ /** Mục dữ liệu ứng với một giá trị — để chip đa chọn lấy `icon`/`prefix`. */
2053
+ itemCua(val: any): SelectOption | null;
2054
+ /** Mục này có phần đầu (icon hoặc chữ) để vẽ không. */
2055
+ coPhanDau(item: SelectOption | null | undefined): boolean;
2056
+ /**
2057
+ * Có mục nào xoá được không — nút "xoá tất cả" neo vào đây chứ không vào
2058
+ * `hasValue()`. Khi mọi mục đã chọn đều `locked`, nút hiện mà bấm không làm
2059
+ * gì: nút chết còn khó hiểu hơn là không có nút.
2060
+ */
2061
+ readonly coTheXoa: _angular_core.Signal<boolean>;
2062
+ /**
2063
+ * Có hiện lối tạo mục mới không.
2064
+ *
2065
+ * Đòi chữ đang gõ KHÔNG trùng nhãn nào đã có (so không phân biệt hoa
2066
+ * thường): còn mục khớp thì người dùng nên chọn mục đó, hiện thêm nút "tạo
2067
+ * mới" cùng tên chỉ làm họ tạo ra bản trùng.
2068
+ */
2069
+ readonly coTheTao: _angular_core.Signal<boolean>;
2070
+ /** Chữ trên nút tạo mới, đã thay `{term}`. */
2071
+ readonly createLabel: _angular_core.Signal<string>;
2072
+ /**
2073
+ * Tạo một mục từ chữ đang gõ rồi chọn luôn.
2074
+ *
2075
+ * KHÔNG tự thêm vào `options` — mảng đó thuộc về nơi gọi, component ghi vào
2076
+ * là sửa dữ liệu của người khác. Chỉ đặt giá trị và phát `added`; nơi gọi
2077
+ * nghe `added` rồi tự quyết có đưa vào danh sách hay không.
2078
+ */
2079
+ taoMuc(): void;
2080
+ readonly waitingForTerm: _angular_core.Signal<boolean>;
2081
+ readonly filteredOptions: _angular_core.Signal<SelectOption[]>;
2082
+ /**
2083
+ * Danh sách PHẲNG gồm tiêu đề nhóm xen giữa các mục.
2084
+ *
2085
+ * Phẳng chứ không lồng hai vòng lặp: `activeIndex` và id của mục đều đánh
2086
+ * theo chỉ số phẳng; lồng vào thì `$index` reset mỗi nhóm và điều hướng bàn
2087
+ * phím sai hết. Cùng lý do đã ghi ở `ol-autocomplete`.
2088
+ */
2089
+ readonly rows: _angular_core.Signal<Dong[]>;
2090
+ /** Chỉ các dòng CHỌN ĐƯỢC — bàn phím chạy trên danh sách này. */
2091
+ readonly selectableItems: _angular_core.Signal<SelectOption[]>;
2092
+ /**
2093
+ * Chỉ số của mục ĐANG CHỌN trong danh sách đã lọc — điểm nhảy tới khi mở
2094
+ * panel. Chưa chọn gì, hoặc mục đang chọn đã bị lọc/ẩn mất, thì về mục đầu.
2095
+ *
2096
+ * Đa chọn: lấy mục chọn ĐẦU TIÊN còn nhìn thấy. `hideSelected` ẩn hết mục
2097
+ * đã chọn nên ở đó luôn rơi về 0 — đúng, vì không còn mục nào để nhảy tới.
2098
+ */
2099
+ private chiSoMucDangChon;
2100
+ /**
2101
+ * Khoảng dòng thật sự được vẽ. Nguyên lý lấy từ `ng-select`:
2102
+ * `scrollTop / itemHeight` cho mục đầu trong tầm nhìn, cộng thêm số dòng
2103
+ * lọt màn hình và vùng đệm hai đầu.
2104
+ *
2105
+ * Tắt `virtualScroll` thì trả trọn danh sách — không có nhánh thứ hai nào
2106
+ * trong template.
2107
+ */
2108
+ readonly viewport: _angular_core.Signal<{
2109
+ rows: Dong[];
2110
+ dauPad: number;
2111
+ cuoiPad: number;
2112
+ }>;
2113
+ onPanelScroll(e: Event): void;
2114
+ onDocumentClick(e: MouseEvent): void;
2115
+ toggle(): void;
2116
+ mo(): void;
2117
+ dong(): void;
2118
+ onSearchInput(val: string): void;
2119
+ /**
2120
+ * Phát `search` có GỠ NẢY. Gõ "nguyễn văn a" là 12 phím — không gỡ nảy thì
2121
+ * 12 lượt gọi API, mà chỉ lượt cuối có ích.
2122
+ */
2123
+ private phatSearch;
2124
+ /** Đơn chọn đóng sau khi chọn, đa chọn thì không — trừ khi nơi gọi chốt khác. */
2125
+ private nenDong;
2126
+ selectItem(item: any): void;
2127
+ /**
2128
+ * Mục này có bị khoá không gỡ không.
2129
+ *
2130
+ * Chặn ở ĐÂY chứ không chỉ ẩn nút × trong template: Backspace cũng gọi
2131
+ * `removeValue`, ẩn nút mà không chặn hàm thì gõ Backspace vẫn gỡ được —
2132
+ * khoá chỉ có tác dụng với chuột.
2133
+ */
2134
+ isLocked(val: any): boolean;
2135
+ removeValue(val: any, e: Event): void;
2136
+ clear(e: Event): void;
2137
+ private datGiaTri;
2138
+ onKeyDown(e: KeyboardEvent): void;
2139
+ private diChuyen;
2140
+ /**
2141
+ * Kéo mục đang đánh dấu vào tầm nhìn.
2142
+ *
2143
+ * Cuộn ảo thì KHÔNG dùng `scrollIntoView` được — mục có thể chưa được vẽ.
2144
+ * Tính thẳng `chiSo * itemHeight` mới đúng.
2145
+ */
2146
+ private cuonToiMuc;
2147
+ optionId(i: number): string;
2148
+ readonly activeOptionId: _angular_core.Signal<string | null>;
2149
+ writeValue(val: any): void;
2150
+ registerOnChange(fn: (val: any) => void): void;
2151
+ registerOnTouched(fn: () => void): void;
2152
+ setDisabledState(isDisabled: boolean): void;
1620
2153
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlSelect, never>;
1621
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlSelect, "ol-select", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "showAsterisk": { "alias": "showAsterisk"; "required": false; "isSignal": true; }; "leftIcon": { "alias": "leftIcon"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; }, never, ["[leftIcon]", "*"], true, never>;
2154
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlSelect, "ol-select", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "showAsterisk": { "alias": "showAsterisk"; "required": false; "isSignal": true; }; "leftIcon": { "alias": "leftIcon"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "searchable": { "alias": "searchable"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "valueKey": { "alias": "valueKey"; "required": false; "isSignal": true; }; "groupBy": { "alias": "groupBy"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; "searchPlaceholder": { "alias": "searchPlaceholder"; "required": false; "isSignal": true; }; "appendTo": { "alias": "appendTo"; "required": false; "isSignal": true; }; "maxSelectedItems": { "alias": "maxSelectedItems"; "required": false; "isSignal": true; }; "closeOnSelect": { "alias": "closeOnSelect"; "required": false; "isSignal": true; }; "hideSelected": { "alias": "hideSelected"; "required": false; "isSignal": true; }; "allowCreate": { "alias": "allowCreate"; "required": false; "isSignal": true; }; "createText": { "alias": "createText"; "required": false; "isSignal": true; }; "groupSeparator": { "alias": "groupSeparator"; "required": false; "isSignal": true; }; "searchPosition": { "alias": "searchPosition"; "required": false; "isSignal": true; }; "searchIcon": { "alias": "searchIcon"; "required": false; "isSignal": true; }; "searchPrefix": { "alias": "searchPrefix"; "required": false; "isSignal": true; }; "triggerIcon": { "alias": "triggerIcon"; "required": false; "isSignal": true; }; "triggerLabel": { "alias": "triggerLabel"; "required": false; "isSignal": true; }; "remoteSearch": { "alias": "remoteSearch"; "required": false; "isSignal": true; }; "minTermLength": { "alias": "minTermLength"; "required": false; "isSignal": true; }; "debounceMs": { "alias": "debounceMs"; "required": false; "isSignal": true; }; "typeToSearchText": { "alias": "typeToSearchText"; "required": false; "isSignal": true; }; "virtualScroll": { "alias": "virtualScroll"; "required": false; "isSignal": true; }; "itemHeight": { "alias": "itemHeight"; "required": false; "isSignal": true; }; "bufferAmount": { "alias": "bufferAmount"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; "search": "search"; "opened": "opened"; "closed": "closed"; "cleared": "cleared"; "added": "added"; "removed": "removed"; "selectionChange": "selectionChange"; }, ["optionTemplate", "labelTemplate"], ["[leftIcon]"], true, never>;
1622
2155
  }
1623
2156
 
1624
- type CheckboxSize = 'sm' | 'md' | 'lg';
2157
+ /**
2158
+ * Thang cỡ theo Figma: `sm | base | lg`. `'md'` là BÍ DANH của `'base'`,
2159
+ * giữ cho mã cũ chạy tiếp và sẽ bỏ ở bản sau — dùng `'base'` cho mã mới.
2160
+ */
2161
+ type CheckboxSize = 'sm' | 'base' | 'lg' | 'md';
1625
2162
  declare class OlCheckbox implements ControlValueAccessor {
1626
2163
  readonly checked: _angular_core.ModelSignal<boolean>;
1627
2164
  readonly label: _angular_core.InputSignal<string>;
@@ -1633,6 +2170,8 @@ declare class OlCheckbox implements ControlValueAccessor {
1633
2170
  */
1634
2171
  readonly disabled: _angular_core.ModelSignal<boolean>;
1635
2172
  readonly size: _angular_core.InputSignal<CheckboxSize>;
2173
+ /** Cỡ đã quy bí danh: `'md'` -> `'base'`. Template so với cái này. */
2174
+ protected readonly resolvedSize: _angular_core.Signal<CheckboxSize>;
1636
2175
  readonly indeterminate: _angular_core.InputSignal<boolean>;
1637
2176
  readonly card: _angular_core.InputSignal<boolean>;
1638
2177
  readonly id: _angular_core.InputSignal<string>;
@@ -1664,7 +2203,11 @@ declare class OlCheckbox implements ControlValueAccessor {
1664
2203
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlCheckbox, "ol-checkbox", never, { "checked": { "alias": "checked"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "indeterminate": { "alias": "indeterminate"; "required": false; "isSignal": true; }; "card": { "alias": "card"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "standalone": { "alias": "standalone"; "required": false; "isSignal": true; }; }, { "checked": "checkedChange"; "disabled": "disabledChange"; }, never, ["*"], true, never>;
1665
2204
  }
1666
2205
 
1667
- type RadioSize = 'sm' | 'md' | 'lg';
2206
+ /**
2207
+ * Thang cỡ theo Figma: `sm | base | lg`. `'md'` là BÍ DANH của `'base'`,
2208
+ * giữ cho mã cũ chạy tiếp và sẽ bỏ ở bản sau — dùng `'base'` cho mã mới.
2209
+ */
2210
+ type RadioSize = 'sm' | 'base' | 'lg' | 'md';
1668
2211
  declare class OlRadio implements ControlValueAccessor {
1669
2212
  readonly name: _angular_core.InputSignal<string>;
1670
2213
  readonly value: _angular_core.InputSignal<any>;
@@ -1679,6 +2222,8 @@ declare class OlRadio implements ControlValueAccessor {
1679
2222
  */
1680
2223
  readonly disabled: _angular_core.ModelSignal<boolean>;
1681
2224
  readonly size: _angular_core.InputSignal<RadioSize>;
2225
+ /** Cỡ đã quy bí danh: `'md'` -> `'base'`. Template so với cái này. */
2226
+ protected readonly resolvedSize: _angular_core.Signal<RadioSize>;
1682
2227
  readonly card: _angular_core.InputSignal<boolean>;
1683
2228
  readonly id: _angular_core.InputSignal<string>;
1684
2229
  readonly checked: _angular_core.InputSignal<boolean | null>;
@@ -1755,7 +2300,8 @@ type TogglePosition = 'start' | 'end';
1755
2300
  * `Settings`, và đoạn mô tả của `Integration` là 16px thay vì 14px. Ở đây dùng
1756
2301
  * 4px và 14px cho cả ba — thiết kế tự mâu thuẫn, không có bản đúng để theo.
1757
2302
  */
1758
- declare class OlToggleInput {
2303
+ type OlToggleInputAlign = 'start' | 'center';
2304
+ declare class OlToggleInput implements ControlValueAccessor {
1759
2305
  readonly checked: _angular_core.ModelSignal<boolean>;
1760
2306
  readonly disabled: _angular_core.ModelSignal<boolean>;
1761
2307
  readonly label: _angular_core.InputSignal<string>;
@@ -1766,8 +2312,22 @@ declare class OlToggleInput {
1766
2312
  readonly icon: _angular_core.InputSignal<string>;
1767
2313
  readonly togglePosition: _angular_core.InputSignal<TogglePosition>;
1768
2314
  /** Figma `Settings` canh giữa theo chiều dọc, hai variant kia canh đầu. */
1769
- readonly align: _angular_core.InputSignal<"center" | "start">;
2315
+ readonly align: _angular_core.InputSignal<OlToggleInputAlign>;
1770
2316
  readonly cardClasses: _angular_core.Signal<string>;
2317
+ /**
2318
+ * Thiếu bốn phương thức này thì `formControlName` / `[formControl]` KHÔNG
2319
+ * gắn được — dùng trong Reactive Forms là lỗi runtime "No value accessor
2320
+ * for form control".
2321
+ */
2322
+ private onChange;
2323
+ private onTouched;
2324
+ /** Lần chạy đầu của `effect` là lúc dựng, chưa phải người dùng đổi. */
2325
+ private daKhoiTao;
2326
+ constructor();
2327
+ writeValue(val: boolean): void;
2328
+ registerOnChange(fn: (val: boolean) => void): void;
2329
+ registerOnTouched(fn: () => void): void;
2330
+ setDisabledState(isDisabled: boolean): void;
1771
2331
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlToggleInput, never>;
1772
2332
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlToggleInput, "ol-toggle-input", never, { "checked": { "alias": "checked"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "helper": { "alias": "helper"; "required": false; "isSignal": true; }; "inputId": { "alias": "inputId"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "togglePosition": { "alias": "togglePosition"; "required": false; "isSignal": true; }; "align": { "alias": "align"; "required": false; "isSignal": true; }; }, { "checked": "checkedChange"; "disabled": "disabledChange"; }, never, ["[logo]"], true, never>;
1773
2333
  }
@@ -1791,7 +2351,11 @@ declare class OlToggleGroup {
1791
2351
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlToggleGroup, "ol-toggle-group", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
1792
2352
  }
1793
2353
 
1794
- type FileInputSize = 'sm' | 'md' | 'lg';
2354
+ /**
2355
+ * Thang cỡ theo Figma: `sm | base | lg`. `'md'` là BÍ DANH của `'base'`,
2356
+ * giữ cho mã cũ chạy tiếp và sẽ bỏ ở bản sau — dùng `'base'` cho mã mới.
2357
+ */
2358
+ type FileInputSize = 'sm' | 'base' | 'lg' | 'md';
1795
2359
  declare class OlFileInput {
1796
2360
  /** Id tự sinh khi nơi gọi không truyền id — để <label for> và
1797
2361
  * aria-describedby luôn nối được với ô nhập. */
@@ -1807,6 +2371,8 @@ declare class OlFileInput {
1807
2371
  readonly title: _angular_core.InputSignal<string>;
1808
2372
  readonly description: _angular_core.InputSignal<string>;
1809
2373
  readonly size: _angular_core.InputSignal<FileInputSize>;
2374
+ /** Cỡ đã quy bí danh: `'md'` -> `'base'`. Template so với cái này. */
2375
+ protected readonly resolvedSize: _angular_core.Signal<FileInputSize>;
1810
2376
  /**
1811
2377
  * Padding theo size. Không dùng [class.p-1.5] trên template vì Angular
1812
2378
  * cắt phần sau dấu chấm — nó render ra "p-1" (4px thay vì 6px).
@@ -1928,8 +2494,12 @@ declare class OlFloatingLabel implements ControlValueAccessor {
1928
2494
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlFloatingLabel, "ol-floating-label", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "errorText": { "alias": "errorText"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "autocomplete": { "alias": "autocomplete"; "required": false; "isSignal": true; }; "labelBg": { "alias": "labelBg"; "required": false; "isSignal": true; }; "prefixIcon": { "alias": "prefixIcon"; "required": false; "isSignal": true; }; "suffixIcon": { "alias": "suffixIcon"; "required": false; "isSignal": true; }; "prefixVariant": { "alias": "prefixVariant"; "required": false; "isSignal": true; }; "suffixVariant": { "alias": "suffixVariant"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; }, never, never, true, never>;
1929
2495
  }
1930
2496
 
1931
- type InputCounterSize = 'sm' | 'md' | 'lg';
1932
- declare class OlInputCounter {
2497
+ /**
2498
+ * Thang cỡ theo Figma: `sm | base | lg`. `'md'` là BÍ DANH của `'base'`,
2499
+ * giữ cho mã cũ chạy tiếp và sẽ bỏ ở bản sau — dùng `'base'` cho mã mới.
2500
+ */
2501
+ type InputCounterSize = 'sm' | 'base' | 'lg' | 'md';
2502
+ declare class OlInputCounter implements ControlValueAccessor {
1933
2503
  /** Id tự sinh khi nơi gọi không truyền id — để <label for> và
1934
2504
  * aria-describedby luôn nối được với ô nhập. */
1935
2505
  private readonly uid;
@@ -1941,15 +2511,60 @@ declare class OlInputCounter {
1941
2511
  readonly min: _angular_core.InputSignal<number>;
1942
2512
  readonly max: _angular_core.InputSignal<number>;
1943
2513
  readonly step: _angular_core.InputSignal<number>;
1944
- readonly disabled: _angular_core.InputSignal<boolean>;
2514
+ /**
2515
+ * `model()` chứ không `input()`: Reactive Forms gọi `setDisabledState()` khi
2516
+ * form khoá/mở control, mà `input()` chỉ đọc nên không ghi được. Cách dùng
2517
+ * từ ngoài KHÔNG đổi.
2518
+ */
2519
+ readonly disabled: _angular_core.ModelSignal<boolean>;
1945
2520
  readonly size: _angular_core.InputSignal<InputCounterSize>;
2521
+ /** Cỡ đã quy bí danh: `'md'` -> `'base'`. Template so với cái này. */
2522
+ protected readonly resolvedSize: _angular_core.Signal<InputCounterSize>;
1946
2523
  readonly label: _angular_core.InputSignal<string>;
1947
2524
  readonly helperText: _angular_core.InputSignal<string>;
1948
2525
  readonly id: _angular_core.InputSignal<string>;
2526
+ /**
2527
+ * Ba cờ dưới đây khớp trục `Type` của Figma `ol-input-counter` (13 Type,
2528
+ * đọc 2026-09-16). Phần lớn Type chỉ khác BỀ RỘNG nên không cần input —
2529
+ * `With buttons` 144px · `Stacked placeholder` 192px · `With spaced buttons`
2530
+ * 320px đều là một ô hai nút. Ba cái này thì khác CẤU TRÚC thật:
2531
+ *
2532
+ * Inline variant HORIZONTAL — nhãn nằm CẠNH khối, không nằm trên
2533
+ * Ghost input ô nhập không nền không viền, khối gap 0 thay vì -1
2534
+ * One button một nút bên phải thay vì hai
2535
+ */
2536
+ /** Nhãn nằm cạnh khối thay vì phía trên (Figma Type=Inline). */
2537
+ readonly inline: _angular_core.InputSignal<boolean>;
2538
+ /** Bỏ nền và viền của khối (Figma Type=Ghost input). */
2539
+ readonly ghost: _angular_core.InputSignal<boolean>;
2540
+ /** Chỉ hiện nút tăng, bỏ nút giảm (Figma Type=One button). */
2541
+ readonly oneButton: _angular_core.InputSignal<boolean>;
2542
+ /** Vỏ ngoài: `inline` xếp ngang, mặc định xếp dọc. */
2543
+ protected readonly wrapperClasses: _angular_core.Signal<"w-full" | "flex w-full items-center gap-3">;
2544
+ /** Nhãn: `inline` bỏ margin dưới vì nó nằm cạnh, không nằm trên. */
2545
+ protected readonly labelClasses: _angular_core.Signal<"shrink-0 text-sm font-medium text-heading" | "block mb-1.5 text-sm font-medium text-heading">;
2546
+ /** Khối nút+ô: `ghost` bỏ nền, viền và đổ bóng. */
2547
+ protected readonly groupClasses: _angular_core.Signal<"relative inline-flex items-center overflow-hidden" | "relative inline-flex items-center rounded-md shadow-xs border border-default bg-neutral-primary overflow-hidden">;
2548
+ /** Nút trong chế độ `ghost` cũng phải bỏ nền và đường ngăn. */
2549
+ protected readonly ghostNut: _angular_core.Signal<"" | "bg-transparent border-0">;
1949
2550
  increment(): void;
1950
2551
  decrement(): void;
2552
+ /**
2553
+ * Thiếu bốn phương thức này thì `formControlName` / `[formControl]` KHÔNG
2554
+ * gắn được — dùng trong Reactive Forms là lỗi runtime "No value accessor
2555
+ * for form control".
2556
+ */
2557
+ private onChange;
2558
+ private onTouched;
2559
+ /** Lần chạy đầu của `effect` là lúc dựng, chưa phải người dùng đổi. */
2560
+ private daKhoiTao;
2561
+ constructor();
2562
+ writeValue(val: number): void;
2563
+ registerOnChange(fn: (val: number) => void): void;
2564
+ registerOnTouched(fn: () => void): void;
2565
+ setDisabledState(isDisabled: boolean): void;
1951
2566
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlInputCounter, never>;
1952
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlInputCounter, "ol-input-counter", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "min": { "alias": "min"; "required": false; "isSignal": true; }; "max": { "alias": "max"; "required": false; "isSignal": true; }; "step": { "alias": "step"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; }, never, never, true, never>;
2567
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlInputCounter, "ol-input-counter", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "min": { "alias": "min"; "required": false; "isSignal": true; }; "max": { "alias": "max"; "required": false; "isSignal": true; }; "step": { "alias": "step"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "inline": { "alias": "inline"; "required": false; "isSignal": true; }; "ghost": { "alias": "ghost"; "required": false; "isSignal": true; }; "oneButton": { "alias": "oneButton"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; }, never, never, true, never>;
1953
2568
  }
1954
2569
 
1955
2570
  /**
@@ -1962,7 +2577,7 @@ declare class OlInputCounter {
1962
2577
  * responsive nào, nên cỡ ô nay do nơi gọi chọn qua `size`.
1963
2578
  */
1964
2579
  type OtpInputSize = 'sm' | 'base' | 'lg' | 'xl';
1965
- declare class OlOtpInput {
2580
+ declare class OlOtpInput implements ControlValueAccessor {
1966
2581
  /** Id tự sinh khi nơi gọi không truyền id — để <label for> và
1967
2582
  * aria-describedby luôn nối được với ô nhập. */
1968
2583
  private readonly uid;
@@ -1972,7 +2587,12 @@ declare class OlOtpInput {
1972
2587
  readonly ariaInvalid: _angular_core.Signal<null>;
1973
2588
  readonly length: _angular_core.InputSignal<number>;
1974
2589
  readonly value: _angular_core.ModelSignal<string>;
1975
- readonly disabled: _angular_core.InputSignal<boolean>;
2590
+ /**
2591
+ * `model()` chứ không `input()`: Reactive Forms gọi `setDisabledState()` khi
2592
+ * form khoá/mở control, mà `input()` chỉ đọc nên không ghi được. Cách dùng
2593
+ * từ ngoài KHÔNG đổi.
2594
+ */
2595
+ readonly disabled: _angular_core.ModelSignal<boolean>;
1976
2596
  readonly label: _angular_core.InputSignal<string>;
1977
2597
  readonly helperText: _angular_core.InputSignal<string>;
1978
2598
  readonly masked: _angular_core.InputSignal<boolean>;
@@ -1998,8 +2618,22 @@ declare class OlOtpInput {
1998
2618
  onKeyDown(event: KeyboardEvent, index: number): void;
1999
2619
  onPaste(event: ClipboardEvent): void;
2000
2620
  private focusIndex;
2621
+ /**
2622
+ * Thiếu bốn phương thức này thì `formControlName` / `[formControl]` KHÔNG
2623
+ * gắn được — dùng trong Reactive Forms là lỗi runtime "No value accessor
2624
+ * for form control".
2625
+ */
2626
+ private onChange;
2627
+ private onTouched;
2628
+ /** Lần chạy đầu của `effect` là lúc dựng, chưa phải người dùng đổi. */
2629
+ private daKhoiTao;
2630
+ constructor();
2631
+ writeValue(val: string): void;
2632
+ registerOnChange(fn: (val: string) => void): void;
2633
+ registerOnTouched(fn: () => void): void;
2634
+ setDisabledState(isDisabled: boolean): void;
2001
2635
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlOtpInput, never>;
2002
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlOtpInput, "ol-otp-input", never, { "length": { "alias": "length"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "masked": { "alias": "masked"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "completed": "completed"; }, never, never, true, never>;
2636
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlOtpInput, "ol-otp-input", never, { "length": { "alias": "length"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "masked": { "alias": "masked"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; "completed": "completed"; }, never, never, true, never>;
2003
2637
  }
2004
2638
 
2005
2639
  /**
@@ -2009,7 +2643,7 @@ declare class OlOtpInput {
2009
2643
  * trục size nào và dùng `rounded-xl` (24px).
2010
2644
  */
2011
2645
  type TagInputSize = 'sm' | 'base' | 'lg' | 'xl';
2012
- declare class OlTagInput {
2646
+ declare class OlTagInput implements ControlValueAccessor {
2013
2647
  /** Id tự sinh khi nơi gọi không truyền id — để <label for> và
2014
2648
  * aria-describedby luôn nối được với ô nhập. */
2015
2649
  private readonly uid;
@@ -2020,7 +2654,12 @@ declare class OlTagInput {
2020
2654
  readonly tags: _angular_core.ModelSignal<string[]>;
2021
2655
  readonly label: _angular_core.InputSignal<string>;
2022
2656
  readonly placeholder: _angular_core.InputSignal<string>;
2023
- readonly disabled: _angular_core.InputSignal<boolean>;
2657
+ /**
2658
+ * `model()` chứ không `input()`: Reactive Forms gọi `setDisabledState()` khi
2659
+ * form khoá/mở control, mà `input()` chỉ đọc nên không ghi được. Cách dùng
2660
+ * từ ngoài KHÔNG đổi — `[disabled]="true"` và `disabled()` vẫn như cũ.
2661
+ */
2662
+ readonly disabled: _angular_core.ModelSignal<boolean>;
2024
2663
  readonly helperText: _angular_core.InputSignal<string>;
2025
2664
  readonly maxTags: _angular_core.InputSignal<number | undefined>;
2026
2665
  readonly id: _angular_core.InputSignal<string>;
@@ -2038,11 +2677,27 @@ declare class OlTagInput {
2038
2677
  addTag(tagText: string): void;
2039
2678
  removeTag(index: number): void;
2040
2679
  focusInput(): void;
2680
+ /**
2681
+ * Bốn phương thức này là thứ Angular Forms gọi tới. Thiếu chúng thì
2682
+ * `formControlName` / `[formControl]` KHÔNG gắn được — dùng component trong
2683
+ * Reactive Forms là lỗi runtime "No value accessor for form control".
2684
+ */
2685
+ private onChange;
2686
+ private onTouched;
2687
+ /** Lần chạy đầu của `effect` không phải người dùng đổi — bỏ qua. */
2688
+ private daKhoiTao;
2689
+ constructor();
2690
+ writeValue(val: string[]): void;
2691
+ registerOnChange(fn: (val: string[]) => void): void;
2692
+ registerOnTouched(fn: () => void): void;
2693
+ setDisabledState(isDisabled: boolean): void;
2694
+ /** Gọi khi rời ô nhập — form dùng để đánh dấu `touched`. */
2695
+ protected danhDauCham(): void;
2041
2696
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlTagInput, never>;
2042
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTagInput, "ol-tag-input", never, { "tags": { "alias": "tags"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "maxTags": { "alias": "maxTags"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, { "tags": "tagsChange"; }, never, never, true, never>;
2697
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTagInput, "ol-tag-input", never, { "tags": { "alias": "tags"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "maxTags": { "alias": "maxTags"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, { "tags": "tagsChange"; "disabled": "disabledChange"; }, never, never, true, never>;
2043
2698
  }
2044
2699
 
2045
- declare class OlRichTextEditor {
2700
+ declare class OlRichTextEditor implements ControlValueAccessor {
2046
2701
  /** Id tự sinh khi nơi gọi không truyền id — để <label for> và
2047
2702
  * aria-describedby luôn nối được với ô nhập. */
2048
2703
  private readonly uid;
@@ -2054,7 +2709,12 @@ declare class OlRichTextEditor {
2054
2709
  readonly content: _angular_core.ModelSignal<string>;
2055
2710
  readonly label: _angular_core.InputSignal<string>;
2056
2711
  readonly placeholder: _angular_core.InputSignal<string>;
2057
- readonly disabled: _angular_core.InputSignal<boolean>;
2712
+ /**
2713
+ * `model()` chứ không `input()`: Reactive Forms gọi `setDisabledState()` khi
2714
+ * form khoá/mở control, mà `input()` chỉ đọc nên không ghi được. Cách dùng
2715
+ * từ ngoài KHÔNG đổi.
2716
+ */
2717
+ readonly disabled: _angular_core.ModelSignal<boolean>;
2058
2718
  readonly helperText: _angular_core.InputSignal<string>;
2059
2719
  readonly minHeight: _angular_core.InputSignal<string>;
2060
2720
  readonly id: _angular_core.InputSignal<string>;
@@ -2065,8 +2725,22 @@ declare class OlRichTextEditor {
2065
2725
  insertList(ordered: boolean): void;
2066
2726
  insertLink(): void;
2067
2727
  insertCodeBlock(): void;
2728
+ /**
2729
+ * Thiếu bốn phương thức này thì `formControlName` / `[formControl]` KHÔNG
2730
+ * gắn được — dùng trong Reactive Forms là lỗi runtime "No value accessor
2731
+ * for form control".
2732
+ */
2733
+ private onChange;
2734
+ private onTouched;
2735
+ /** Lần chạy đầu của `effect` là lúc dựng, chưa phải người dùng đổi. */
2736
+ private daKhoiTao;
2737
+ constructor();
2738
+ writeValue(val: string): void;
2739
+ registerOnChange(fn: (val: string) => void): void;
2740
+ registerOnTouched(fn: () => void): void;
2741
+ setDisabledState(isDisabled: boolean): void;
2068
2742
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlRichTextEditor, never>;
2069
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlRichTextEditor, "ol-rich-text-editor", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "content": { "alias": "content"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "minHeight": { "alias": "minHeight"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "content": "contentChange"; }, never, never, true, never>;
2743
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlRichTextEditor, "ol-rich-text-editor", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "content": { "alias": "content"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "minHeight": { "alias": "minHeight"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "content": "contentChange"; "disabled": "disabledChange"; }, never, never, true, never>;
2070
2744
  }
2071
2745
 
2072
2746
  type TimepickerFormat = '24h' | '12h';
@@ -2083,6 +2757,7 @@ interface TimeObject {
2083
2757
  period?: 'AM' | 'PM';
2084
2758
  formatted: string;
2085
2759
  }
2760
+ type OlTimepickerAddonType = 'none' | 'left' | 'right';
2086
2761
  declare class OlTimepicker implements ControlValueAccessor {
2087
2762
  private readonly elementRef;
2088
2763
  readonly value: _angular_core.ModelSignal<string>;
@@ -2104,8 +2779,21 @@ declare class OlTimepicker implements ControlValueAccessor {
2104
2779
  readonly errorText: _angular_core.InputSignal<string>;
2105
2780
  readonly id: _angular_core.InputSignal<string>;
2106
2781
  readonly minuteStep: _angular_core.InputSignal<number>;
2782
+ /**
2783
+ * Đẩy bảng chọn giờ ra `<body>` thay vì để trong cây DOM của component.
2784
+ *
2785
+ * Cần khi component nằm trong modal / drawer / bảng có `overflow: hidden`:
2786
+ * khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING CONTEXT —
2787
+ * z-index của lớp nổi không thoát ra được — và `overflow: hidden` CẮT luôn
2788
+ * phần tràn. Hai lỗi chồng nhau, z-index một mình không chữa nổi cái thứ
2789
+ * hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
2790
+ *
2791
+ * Mặc định TẮT để không đổi hành vi mã đang chạy. Cùng tên và cùng ý nghĩa
2792
+ * với `appendTo` của ng-select và PrimeNG.
2793
+ */
2794
+ readonly appendTo: _angular_core.InputSignal<"body" | null>;
2107
2795
  readonly showPresets: _angular_core.InputSignal<boolean>;
2108
- readonly addonType: _angular_core.InputSignal<"none" | "left" | "right">;
2796
+ readonly addonType: _angular_core.InputSignal<OlTimepickerAddonType>;
2109
2797
  readonly addonText: _angular_core.InputSignal<string>;
2110
2798
  readonly addonIcon: _angular_core.InputSignal<string>;
2111
2799
  private readonly generatedId;
@@ -2113,6 +2801,12 @@ declare class OlTimepicker implements ControlValueAccessor {
2113
2801
  readonly helperId: _angular_core.Signal<string>;
2114
2802
  readonly describedBy: _angular_core.Signal<string | null>;
2115
2803
  readonly ariaInvalid: _angular_core.Signal<"true" | null>;
2804
+ private readonly panel;
2805
+ private readonly overlayContainer;
2806
+ private readonly positioning;
2807
+ /** Hàm trả panel về chỗ cũ + hàm ngắt theo dõi vị trí. */
2808
+ private goPanel;
2809
+ private ngatTheoDoi;
2116
2810
  readonly isOpen: _angular_core.WritableSignal<boolean>;
2117
2811
  readonly selectedHour: _angular_core.WritableSignal<number>;
2118
2812
  readonly selectedMinute: _angular_core.WritableSignal<number>;
@@ -2136,6 +2830,8 @@ declare class OlTimepicker implements ControlValueAccessor {
2136
2830
  private onTouched;
2137
2831
  private emitAllChanges;
2138
2832
  constructor(elementRef: ElementRef);
2833
+ /** Ngắt theo dõi vị trí và trả panel về chỗ cũ. Gọi được nhiều lần. */
2834
+ private donDepPanel;
2139
2835
  onDocumentClick(event: MouseEvent): void;
2140
2836
  toggleDropdown(): void;
2141
2837
  private scrollToSelected;
@@ -2154,10 +2850,14 @@ declare class OlTimepicker implements ControlValueAccessor {
2154
2850
  registerOnTouched(fn: any): void;
2155
2851
  setDisabledState(isDisabled: boolean): void;
2156
2852
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlTimepicker, never>;
2157
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTimepicker, "ol-timepicker", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "format": { "alias": "format"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "showAsterisk": { "alias": "showAsterisk"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "errorText": { "alias": "errorText"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "minuteStep": { "alias": "minuteStep"; "required": false; "isSignal": true; }; "showPresets": { "alias": "showPresets"; "required": false; "isSignal": true; }; "addonType": { "alias": "addonType"; "required": false; "isSignal": true; }; "addonText": { "alias": "addonText"; "required": false; "isSignal": true; }; "addonIcon": { "alias": "addonIcon"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; "timeChange": "timeChange"; "timestampChange": "timestampChange"; "isoChange": "isoChange"; "timeObjectChange": "timeObjectChange"; }, never, never, true, never>;
2853
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTimepicker, "ol-timepicker", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "format": { "alias": "format"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "showAsterisk": { "alias": "showAsterisk"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "errorText": { "alias": "errorText"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "minuteStep": { "alias": "minuteStep"; "required": false; "isSignal": true; }; "appendTo": { "alias": "appendTo"; "required": false; "isSignal": true; }; "showPresets": { "alias": "showPresets"; "required": false; "isSignal": true; }; "addonType": { "alias": "addonType"; "required": false; "isSignal": true; }; "addonText": { "alias": "addonText"; "required": false; "isSignal": true; }; "addonIcon": { "alias": "addonIcon"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; "timeChange": "timeChange"; "timestampChange": "timestampChange"; "isoChange": "isoChange"; "timeObjectChange": "timeObjectChange"; }, never, never, true, never>;
2158
2854
  }
2159
2855
 
2160
- type AutocompleteSize = 'sm' | 'md' | 'lg';
2856
+ /**
2857
+ * Thang cỡ theo Figma: `sm | base | lg`. `'md'` là BÍ DANH của `'base'`,
2858
+ * giữ cho mã cũ chạy tiếp và sẽ bỏ ở bản sau — dùng `'base'` cho mã mới.
2859
+ */
2860
+ type AutocompleteSize = 'sm' | 'base' | 'lg' | 'md';
2161
2861
  type AutocompleteState = 'default' | 'error' | 'success';
2162
2862
  interface AutocompleteOption {
2163
2863
  label: string;
@@ -2187,6 +2887,8 @@ declare class OlAutocomplete implements ControlValueAccessor {
2187
2887
  readonly readonly: _angular_core.InputSignal<boolean>;
2188
2888
  readonly loading: _angular_core.InputSignal<boolean>;
2189
2889
  readonly size: _angular_core.InputSignal<AutocompleteSize>;
2890
+ /** Cỡ đã quy bí danh: `'md'` -> `'base'`. Template so với cái này. */
2891
+ protected readonly resolvedSize: _angular_core.Signal<AutocompleteSize>;
2190
2892
  readonly state: _angular_core.InputSignal<AutocompleteState>;
2191
2893
  readonly required: _angular_core.InputSignal<boolean>;
2192
2894
  readonly showAsterisk: _angular_core.InputSignal<boolean>;
@@ -2195,8 +2897,50 @@ declare class OlAutocomplete implements ControlValueAccessor {
2195
2897
  readonly id: _angular_core.InputSignal<string>;
2196
2898
  readonly allowCreate: _angular_core.InputSignal<boolean>;
2197
2899
  readonly emptyText: _angular_core.InputSignal<string>;
2900
+ /**
2901
+ * Đẩy danh sách gợi ý ra `<body>` thay vì để trong cây DOM của component.
2902
+ *
2903
+ * Cần khi component nằm trong modal / drawer / bảng có `overflow: hidden`:
2904
+ * khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING CONTEXT —
2905
+ * z-index của lớp nổi không thoát ra được — và `overflow: hidden` CẮT luôn
2906
+ * phần tràn. Hai lỗi chồng nhau, z-index một mình không chữa nổi cái thứ
2907
+ * hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
2908
+ *
2909
+ * Mặc định TẮT để không đổi hành vi mã đang chạy. Cùng tên và cùng ý nghĩa
2910
+ * với `appendTo` của ng-select và PrimeNG.
2911
+ */
2912
+ readonly appendTo: _angular_core.InputSignal<"body" | null>;
2198
2913
  readonly groupBy: _angular_core.InputSignal<string>;
2914
+ /**
2915
+ * LỌC TỪ XA. Bật lên thì component NGỪNG tự lọc `options` — nó chỉ phát
2916
+ * `search` và hiển thị đúng những gì nơi gọi truyền lại vào `options`.
2917
+ *
2918
+ * Học cấu trúc `typeahead` của `ng-select` nhưng không mượn mã: ng-select
2919
+ * nhận một `Subject` để nơi gọi tự nối toán tử RxJS. Ở đây dùng `output` +
2920
+ * `minTermLength` + `debounceMs` để nơi gọi chỉ cần một hàm bắt sự kiện,
2921
+ * không phải biết RxJS — hợp với lối signal của olkit.
2922
+ */
2923
+ readonly remoteSearch: _angular_core.InputSignal<boolean>;
2924
+ /** Số ký tự tối thiểu mới phát `search`. Dưới mức này danh sách để trống. */
2925
+ readonly minTermLength: _angular_core.InputSignal<number>;
2926
+ /** Chờ ngừng gõ bao lâu (ms) mới phát `search`. 0 = phát ngay mỗi phím. */
2927
+ readonly debounceMs: _angular_core.InputSignal<number>;
2928
+ /** Chữ hiện khi `remoteSearch` đang chờ đủ `minTermLength`. */
2929
+ readonly typeToSearchText: _angular_core.InputSignal<string>;
2199
2930
  readonly createOption: _angular_core.OutputEmitterRef<string>;
2931
+ /**
2932
+ * Bảy sự kiện dưới đây trước nay KHÔNG có — nơi dùng chỉ bắt được
2933
+ * `createOption`, nên không biết lúc nào người dùng mở danh sách, gỡ một thẻ
2934
+ * hay xoá trắng. Đối chiếu `ng-select` 24.1.2 (đọc `.d.ts` từ npm): nó có 16
2935
+ * output, ở đây lấy 7 cái thật sự dùng tới.
2936
+ */
2937
+ readonly search: _angular_core.OutputEmitterRef<string>;
2938
+ readonly opened: _angular_core.OutputEmitterRef<void>;
2939
+ readonly closed: _angular_core.OutputEmitterRef<void>;
2940
+ readonly cleared: _angular_core.OutputEmitterRef<void>;
2941
+ readonly added: _angular_core.OutputEmitterRef<any>;
2942
+ readonly removed: _angular_core.OutputEmitterRef<any>;
2943
+ readonly selectionChange: _angular_core.OutputEmitterRef<any>;
2200
2944
  optionTemplate?: TemplateRef<any>;
2201
2945
  private readonly generatedId;
2202
2946
  protected readonly inputId: _angular_core.Signal<string>;
@@ -2213,8 +2957,35 @@ declare class OlAutocomplete implements ControlValueAccessor {
2213
2957
  readonly activeIndex: _angular_core.WritableSignal<number>;
2214
2958
  private onChange;
2215
2959
  private onTouched;
2960
+ private readonly panel;
2961
+ private readonly overlayContainer;
2962
+ private readonly positioning;
2963
+ /** Hàm trả panel về chỗ cũ + hàm ngắt theo dõi vị trí. */
2964
+ private goPanel;
2965
+ private ngatTheoDoi;
2966
+ private readonly destroyRef;
2967
+ /** Trạng thái mở ở lượt chạy trước — để `effect` chỉ phát khi ĐỔI. */
2968
+ private daMo;
2969
+ /** Hẹn giờ gỡ nảy cho `search`. */
2970
+ private hengio;
2216
2971
  constructor(elementRef: ElementRef);
2972
+ /** Ngắt theo dõi vị trí và trả panel về chỗ cũ. Gọi được nhiều lần. */
2973
+ private donDepPanel;
2217
2974
  onDocumentClick(event: MouseEvent): void;
2975
+ /**
2976
+ * Chưa gõ đủ `minTermLength` ở chế độ từ xa — template hiện
2977
+ * `typeToSearchText` thay vì `emptyText`. Hai thông báo khác nghĩa hẳn nhau:
2978
+ * "gõ thêm đi" không phải "không tìm thấy".
2979
+ */
2980
+ readonly waitingForTerm: _angular_core.Signal<boolean>;
2981
+ /**
2982
+ * `minTermLength` đã lọc sạch. `[minTermLength]="undefined"` (bind vào một
2983
+ * khoá state chưa có) đè mất giá trị mặc định, và `x < undefined` là `NaN`
2984
+ * — luôn `false`, nên chế độ chờ không bao giờ bật. Cùng lớp lỗi BUG-009.
2985
+ */
2986
+ private readonly safeMinTerm;
2987
+ /** `debounceMs` đã lọc sạch — xem lý do ở `safeMinTerm`. */
2988
+ private readonly safeDebounce;
2218
2989
  readonly filteredOptions: _angular_core.Signal<any[]>;
2219
2990
  /**
2220
2991
  * Gom gợi ý thành nhóm theo khoá `groupBy`, cùng lối với `labelKey`/`valueKey`
@@ -2244,6 +3015,17 @@ declare class OlAutocomplete implements ControlValueAccessor {
2244
3015
  private areValuesEqual;
2245
3016
  onInputFocus(): void;
2246
3017
  onSearchChange(val: string): void;
3018
+ /**
3019
+ * Phát `search` có GỠ NẢY. Gõ "nguyễn văn a" là 12 phím — không gỡ nảy thì
3020
+ * 12 lượt gọi API, mà chỉ lượt cuối có ích.
3021
+ *
3022
+ * Huỷ hẹn giờ cũ trước khi đặt mới, và huỷ nốt khi component bị huỷ
3023
+ * (`destroyRef` ở constructor) — nếu không, gõ xong rồi rời trang thì
3024
+ * `emit` chạy trên một component đã chết.
3025
+ *
3026
+ * Dưới `minTermLength` thì KHÔNG phát: nơi gọi không phải tự kiểm lại.
3027
+ */
3028
+ private phatSearch;
2247
3029
  selectOption(item: any): void;
2248
3030
  removeTag(valToRemove: any, event: MouseEvent): void;
2249
3031
  clearAll(event: MouseEvent): void;
@@ -2255,7 +3037,7 @@ declare class OlAutocomplete implements ControlValueAccessor {
2255
3037
  registerOnTouched(fn: any): void;
2256
3038
  setDisabledState(isDisabled: boolean): void;
2257
3039
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlAutocomplete, never>;
2258
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlAutocomplete, "ol-autocomplete", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "valueKey": { "alias": "valueKey"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "showAsterisk": { "alias": "showAsterisk"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "errorText": { "alias": "errorText"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "allowCreate": { "alias": "allowCreate"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; "groupBy": { "alias": "groupBy"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; "createOption": "createOption"; }, ["optionTemplate"], never, true, never>;
3040
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlAutocomplete, "ol-autocomplete", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "valueKey": { "alias": "valueKey"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "showAsterisk": { "alias": "showAsterisk"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "errorText": { "alias": "errorText"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "allowCreate": { "alias": "allowCreate"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; "appendTo": { "alias": "appendTo"; "required": false; "isSignal": true; }; "groupBy": { "alias": "groupBy"; "required": false; "isSignal": true; }; "remoteSearch": { "alias": "remoteSearch"; "required": false; "isSignal": true; }; "minTermLength": { "alias": "minTermLength"; "required": false; "isSignal": true; }; "debounceMs": { "alias": "debounceMs"; "required": false; "isSignal": true; }; "typeToSearchText": { "alias": "typeToSearchText"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; "createOption": "createOption"; "search": "search"; "opened": "opened"; "closed": "closed"; "cleared": "cleared"; "added": "added"; "removed": "removed"; "selectionChange": "selectionChange"; }, ["optionTemplate"], never, true, never>;
2259
3041
  }
2260
3042
 
2261
3043
  declare class OlKbd {
@@ -2295,13 +3077,32 @@ declare class OlListGroupItem {
2295
3077
  */
2296
3078
  private readonly parentListGroup;
2297
3079
  readonly hasDivider: _angular_core.Signal<boolean>;
3080
+ /**
3081
+ * Màu chữ theo trục `State` của Figma #1149:70209 (đọc 2026-09-16):
3082
+ *
3083
+ * Initial #4a5565 -> `text-body`
3084
+ * Hover/Active #101828 -> `text-heading`
3085
+ * Disabled #99a1af -> `text-fg-disabled`
3086
+ *
3087
+ * Đo trước khi sửa: mục thường ra `rgb(16,24,40)` — tức là ĐÃ bằng màu
3088
+ * Hover/Active, nên hover không đổi gì và mục thường đậm hơn Figma. Nguyên
3089
+ * nhân: component không đặt màu cho trạng thái thường, chữ thừa kế màu của
3090
+ * trang. `Disabled` thì vốn đã khớp đúng.
3091
+ *
3092
+ * Gộp thành MỘT chuỗi thay vì nhiều `[class.X]`: class tĩnh và class binding
3093
+ * cùng thuộc tính tranh nhau theo thứ tự luật CSS chứ không theo thứ tự viết,
3094
+ * và hai `[class.X]` trùng tên bị Angular gộp — cái sau thắng im lặng.
3095
+ */
3096
+ readonly textClasses: _angular_core.Signal<"text-heading font-semibold" | "cursor-not-allowed text-fg-disabled" | "text-body hover:text-heading">;
2298
3097
  handleClick(event: MouseEvent): void;
2299
3098
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlListGroupItem, never>;
2300
3099
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlListGroupItem, "ol-list-group-item", never, { "active": { "alias": "active"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; }, { "itemClick": "itemClick"; }, never, ["*"], true, never>;
2301
3100
  }
2302
3101
 
2303
- type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | '2xl';
3102
+ type ModalSize = 'sm' | 'base' | 'lg' | 'xl' | '2xl';
2304
3103
  type ModalPlacement = 'center' | 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
3104
+ type OlModalBackdrop = 'static' | 'dynamic';
3105
+ type OlModalRole = 'dialog' | 'alertdialog';
2305
3106
  declare class OlModal {
2306
3107
  private readonly focusTrap;
2307
3108
  private readonly destroyRef;
@@ -2318,14 +3119,14 @@ declare class OlModal {
2318
3119
  readonly title: _angular_core.InputSignal<string>;
2319
3120
  readonly size: _angular_core.InputSignal<ModalSize>;
2320
3121
  readonly dismissible: _angular_core.InputSignal<boolean>;
2321
- readonly backdrop: _angular_core.InputSignal<"static" | "dynamic">;
3122
+ readonly backdrop: _angular_core.InputSignal<OlModalBackdrop>;
2322
3123
  readonly placement: _angular_core.InputSignal<ModalPlacement>;
2323
3124
  /**
2324
3125
  * Vai trò ARIA. `alertdialog` dành cho hộp thoại BẮT BUỘC trả lời — bộ đọc
2325
3126
  * màn hình đọc luôn phần mô tả và không cho lướt qua như `dialog` thường.
2326
3127
  * `ol-alert-dialog` đặt giá trị này; nơi khác cứ để mặc định.
2327
3128
  */
2328
- readonly role: _angular_core.InputSignal<"dialog" | "alertdialog">;
3129
+ readonly role: _angular_core.InputSignal<OlModalRole>;
2329
3130
  readonly opened: _angular_core.OutputEmitterRef<void>;
2330
3131
  readonly closed: _angular_core.OutputEmitterRef<void>;
2331
3132
  constructor();
@@ -2369,6 +3170,17 @@ type ModalFooterLayout = 'buttons' | 'three-buttons' | 'buttons-links' | 'pagina
2369
3170
  * Dải nút hành động cuối Modal — khớp Figma `ol-modal-footer`.
2370
3171
  * Figma có 4 biến thể: Buttons, 3 buttons, Buttons & links, Pagination.
2371
3172
  * Nút thật do nơi dùng truyền vào qua <ng-content>; component lo bố cục.
3173
+ *
3174
+ * KHÔNG lắp vào slot `[footer]` của `ol-modal`. Slot đó ĐÃ nằm sẵn trong một
3175
+ * khung `p-5 border-t bg-neutral-secondary` do chính `ol-modal` dựng, nên lắp
3176
+ * thêm component này vào là LỒNG HAI KHUNG. Đo /modal (2026-09-16): đệm cộng
3177
+ * dồn 20 + 20 = 40px, hai viền trên 1px chồng nhau, nền lặp hai lần.
3178
+ *
3179
+ * Dùng nó khi tự dựng hộp thoại (không qua `ol-modal`), hoặc trong `ol-drawer`
3180
+ * — `ol-drawer` chỉ có `<ng-content />` TRẦN nên ghép vừa khít. Đó là lý do
3181
+ * `ol-drawer-footer` dùng được 9 lượt trong docs còn cái này 0 lượt.
3182
+ *
3183
+ * Với `ol-modal`, cách đúng là truyền nút trực tiếp: `<div footer>…</div>`.
2372
3184
  */
2373
3185
  declare class OlModalFooter {
2374
3186
  readonly layout: _angular_core.InputSignal<ModalFooterLayout>;
@@ -2457,6 +3269,18 @@ declare class OlPagination {
2457
3269
  * xảy ra. `type` đặt tường minh vẫn thắng; cờ chỉ điền vào chỗ trống.
2458
3270
  */
2459
3271
  readonly effectiveType: _angular_core.Signal<PaginationType>;
3272
+ /**
3273
+ * Trang đang xem — gộp `pageIndex` và bí danh `currentPage`.
3274
+ *
3275
+ * Bản cũ viết `pageIndex() || currentPage() || 1`. Vì `pageIndex` mặc định
3276
+ * là **1** (giá trị thật, không phải 0), vế trái LUÔN đúng, nên
3277
+ * `currentPage` chỉ có tác dụng khi `pageIndex` bằng 0 — tức không bao giờ
3278
+ * trong cách dùng bình thường. Đo /pagination: đặt `[currentPage]="5"` mà
3279
+ * trang vẫn ở 1, bí danh công khai nhưng vô dụng.
3280
+ *
3281
+ * Nay so với MẶC ĐỊNH: cái nào khác 1 thì đó là ý người dùng. `goToPage`
3282
+ * đặt cả hai nên sau lần bấm đầu chúng luôn bằng nhau, không có xung đột.
3283
+ */
2460
3284
  readonly effectivePageIndex: _angular_core.Signal<number>;
2461
3285
  /**
2462
3286
  * Luôn trả số nguyên trong [1, 5000].
@@ -2471,14 +3295,48 @@ declare class OlPagination {
2471
3295
  * `ol-carousel`.
2472
3296
  */
2473
3297
  readonly calculatedTotalPages: _angular_core.Signal<number>;
2474
- /** Mảng cho `@for` — dựng ở đây thay vì `[].constructor()` trong template. */
3298
+ /**
3299
+ * Mảng cho `@for` của ô chọn trang — dựng ở đây thay vì `[].constructor()`.
3300
+ *
3301
+ * CHẶN TRẦN 200 mục. `calculatedTotalPages` đã chặn ở 5000, đủ để không treo
3302
+ * trang, nhưng 5000 `<option>` trong MỘT `<select>` thì không ai chọn nổi:
3303
+ * đo 2026-09-17 với `total=100000` ra đúng 5000 thẻ `<option>`.
3304
+ *
3305
+ * Khi vượt trần, ô chọn chỉ liệt kê cửa sổ quanh trang hiện tại — người dùng
3306
+ * vẫn nhảy xa được bằng Type `With input` (ô nhập số trang). Luôn kèm trang
3307
+ * hiện tại, nếu không `[ngModel]` không khớp option nào và ô chọn hiện rỗng.
3308
+ */
2475
3309
  readonly pageList: _angular_core.Signal<number[]>;
2476
3310
  readonly startRecord: _angular_core.Signal<number>;
2477
3311
  readonly endRecord: _angular_core.Signal<number>;
2478
3312
  readonly visiblePages: _angular_core.Signal<(number | "ellipsis")[]>;
3313
+ /**
3314
+ * Khoá `track` cho dãy nút số.
3315
+ *
3316
+ * `track $index` khiến Angular tái dùng CÙNG phần tử DOM cho một ý nghĩa
3317
+ * khác. Nội dung vẫn vẽ đúng nên nhìn không ra, nhưng FOCUS thì ở lại:
3318
+ * đo 2026-09-17 — người dùng tab tới nút "4", đổi sang trang 10, focus vẫn
3319
+ * nằm trên đúng phần tử đó mà nút giờ mang số **"10"**. Bấm Enter là nhảy
3320
+ * tới một trang khác hẳn trang mình đang nhìn.
3321
+ *
3322
+ * Không track thuần theo `page` được: `'ellipsis'` xuất hiện HAI lần trong
3323
+ * cùng một danh sách (`[1, …, 9, 10, 11, …, 20]`) nên khoá sẽ trùng. Ghép
3324
+ * thêm vị trí cho riêng nó là đủ phân biệt mà vẫn giữ khoá ổn định cho số.
3325
+ */
3326
+ trackPage(index: number, page: number | 'ellipsis'): string;
2479
3327
  goToPage(page: number): void;
2480
3328
  prev(): void;
2481
3329
  next(): void;
3330
+ /**
3331
+ * Đổi số dòng mỗi trang. Nhận thẳng SỐ vì template dùng `(ngModelChange)`,
3332
+ * không còn `(change)` với `event.target`.
3333
+ *
3334
+ * `goToPage(1)` KHÔNG phát được sự kiện khi đang ở trang 1 (nó bỏ qua nếu
3335
+ * trang không đổi), nên đặt `pageIndex` trực tiếp: đổi cỡ trang luôn phải
3336
+ * đưa về trang đầu, còn đang ở đâu thì đưa về không phải việc của người gọi.
3337
+ */
3338
+ onPageSizeChange(newSize: number): void;
3339
+ /** @deprecated Dùng `onPageSizeChange(number)`. Giữ cho mã ngoài đang gọi. */
2482
3340
  onPageSizeSelect(event: Event): void;
2483
3341
  handleJump(): void;
2484
3342
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlPagination, never>;
@@ -2503,6 +3361,19 @@ declare class OlPopover {
2503
3361
  */
2504
3362
  readonly headingLevel: _angular_core.InputSignal<1 | 2 | 3 | 4 | 5 | 6>;
2505
3363
  readonly title: _angular_core.InputSignal<string>;
3364
+ /**
3365
+ * Đẩy panel ra `<body>` thay vì để trong cây DOM của component.
3366
+ *
3367
+ * Cần khi component nằm trong modal / drawer / bảng có `overflow: hidden`:
3368
+ * khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING CONTEXT —
3369
+ * z-index của lớp nổi không thoát ra được — và `overflow: hidden` CẮT luôn
3370
+ * phần tràn. Hai lỗi chồng nhau, z-index một mình không chữa nổi cái thứ
3371
+ * hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
3372
+ *
3373
+ * Mặc định TẮT để không đổi hành vi mã đang chạy. Cùng tên và cùng ý nghĩa
3374
+ * với `appendTo` của ng-select và PrimeNG.
3375
+ */
3376
+ readonly appendTo: _angular_core.InputSignal<"body" | null>;
2506
3377
  readonly placement: _angular_core.InputSignal<PopoverPlacement>;
2507
3378
  readonly trigger: _angular_core.InputSignal<PopoverTrigger>;
2508
3379
  readonly offset: _angular_core.InputSignal<number>;
@@ -2517,7 +3388,10 @@ declare class OlPopover {
2517
3388
  readonly closeDelay: _angular_core.InputSignal<number>;
2518
3389
  readonly opened: _angular_core.OutputEmitterRef<void>;
2519
3390
  readonly closed: _angular_core.OutputEmitterRef<void>;
3391
+ private readonly overlayContainer;
2520
3392
  private cleanupAutoUpdate;
3393
+ /** Hàm trả panel về chỗ cũ khi đóng. */
3394
+ private goPanel;
2521
3395
  constructor();
2522
3396
  toggle(): void;
2523
3397
  open(): void;
@@ -2537,7 +3411,7 @@ declare class OlPopover {
2537
3411
  onTriggerClick(): void;
2538
3412
  private stopAutoUpdate;
2539
3413
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlPopover, never>;
2540
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlPopover, "ol-popover", never, { "headingLevel": { "alias": "headingLevel"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "placement": { "alias": "placement"; "required": false; "isSignal": true; }; "trigger": { "alias": "trigger"; "required": false; "isSignal": true; }; "offset": { "alias": "offset"; "required": false; "isSignal": true; }; "closeOnOutsideClick": { "alias": "closeOnOutsideClick"; "required": false; "isSignal": true; }; "closeOnEscape": { "alias": "closeOnEscape"; "required": false; "isSignal": true; }; "isOpen": { "alias": "isOpen"; "required": false; "isSignal": true; }; "openDelay": { "alias": "openDelay"; "required": false; "isSignal": true; }; "closeDelay": { "alias": "closeDelay"; "required": false; "isSignal": true; }; }, { "isOpen": "isOpenChange"; "opened": "opened"; "closed": "closed"; }, never, ["[trigger]", "*"], true, never>;
3414
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlPopover, "ol-popover", never, { "headingLevel": { "alias": "headingLevel"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "appendTo": { "alias": "appendTo"; "required": false; "isSignal": true; }; "placement": { "alias": "placement"; "required": false; "isSignal": true; }; "trigger": { "alias": "trigger"; "required": false; "isSignal": true; }; "offset": { "alias": "offset"; "required": false; "isSignal": true; }; "closeOnOutsideClick": { "alias": "closeOnOutsideClick"; "required": false; "isSignal": true; }; "closeOnEscape": { "alias": "closeOnEscape"; "required": false; "isSignal": true; }; "isOpen": { "alias": "isOpen"; "required": false; "isSignal": true; }; "openDelay": { "alias": "openDelay"; "required": false; "isSignal": true; }; "closeDelay": { "alias": "closeDelay"; "required": false; "isSignal": true; }; }, { "isOpen": "isOpenChange"; "opened": "opened"; "closed": "closed"; }, never, ["[trigger]", "*"], true, never>;
2541
3415
  }
2542
3416
 
2543
3417
  /**
@@ -2574,7 +3448,7 @@ declare class OlPopoverContent {
2574
3448
  * `ol-button` màu `light`/`purple`.
2575
3449
  */
2576
3450
  type ProgressColor = 'brand' | 'gray' | 'dark' | 'success' | 'danger' | 'warning' | 'purple' | 'indigo';
2577
- type ProgressSize = 'sm' | 'md' | 'lg' | 'xl';
3451
+ type ProgressSize = 'sm' | 'base' | 'lg' | 'xl';
2578
3452
  /** Trục `Type` của Figma: nhãn nằm trên, cùng hàng, hay dưới thanh. */
2579
3453
  type ProgressLabelPosition = 'top' | 'side' | 'bottom';
2580
3454
  declare class OlProgress {
@@ -2587,13 +3461,13 @@ declare class OlProgress {
2587
3461
  * trên template: Angular cắt phần sau dấu chấm nên [class.h-1.5] render
2588
3462
  * ra "h-1" (4px thay vì 6px) và [class.h-2.5] ra "h-2" (8px thay vì 10px).
2589
3463
  */
2590
- readonly heightClass: _angular_core.Signal<"h-1.5" | "h-4" | "h-6" | "h-2.5">;
3464
+ readonly heightClass: _angular_core.Signal<"h-4" | "h-1.5" | "h-6" | "h-2.5">;
2591
3465
  /**
2592
3466
  * Gom màu vào MỘT chuỗi thay vì nhiều `[class.bg-X]`: hai class nền cùng
2593
3467
  * lúc thì trình duyệt chọn theo thứ tự luật trong file CSS chứ không theo
2594
3468
  * thứ tự class, nên nhánh phải loại trừ nhau ngay từ chỗ sinh chuỗi.
2595
3469
  */
2596
- readonly barColorClass: _angular_core.Signal<"bg-warning-surface" | "bg-success-surface" | "bg-danger-surface" | "bg-brand" | "bg-dark" | "bg-purple-600" | "bg-gray-surface" | "bg-indigo-600">;
3470
+ readonly barColorClass: _angular_core.Signal<"bg-warning-surface" | "bg-success-surface" | "bg-danger-surface" | "bg-brand" | "bg-dark" | "bg-gray-surface" | "bg-purple-600" | "bg-indigo-600">;
2597
3471
  readonly label: _angular_core.InputSignal<string>;
2598
3472
  readonly showProgress: _angular_core.InputSignal<boolean>;
2599
3473
  readonly hasLabel: _angular_core.Signal<boolean>;
@@ -2641,7 +3515,7 @@ declare class OlRating {
2641
3515
  * Cũng chặn số quá lớn: `max = 999999` dựng gần một triệu phần tử và treo
2642
3516
  * trang. Trần 50 sao là quá đủ cho mọi thang đánh giá.
2643
3517
  */
2644
- private readonly maxSach;
3518
+ private readonly safeMax;
2645
3519
  readonly stars: _angular_core.Signal<number[]>;
2646
3520
  /** Figma vẽ 0..max, tức max + 1 nút. */
2647
3521
  readonly radioValues: _angular_core.Signal<number[]>;
@@ -2739,6 +3613,10 @@ declare class OlSidebarSubmenu {
2739
3613
  * <ol-skeleton type="text" [width]="i === 4 ? '60%' : '100%'" />
2740
3614
  * }
2741
3615
  */
3616
+ type OlSkeletonType = 'text' | 'avatar' | 'card' | 'button' | 'image' | 'custom';
3617
+ type OlSkeletonRounded = 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
3618
+ type OlSkeletonAnimation = 'pulse' | 'wave' | 'none';
3619
+ type OlSkeletonAspectRatio = '16/9' | '4/3';
2742
3620
  declare class OlSkeleton {
2743
3621
  /**
2744
3622
  * Loại skeleton
@@ -2749,17 +3627,17 @@ declare class OlSkeleton {
2749
3627
  * - image: Tỷ lệ 16:9 hoặc 4:3
2750
3628
  * - custom: Tùy chỉnh width/height
2751
3629
  */
2752
- type: _angular_core.InputSignal<"custom" | "button" | "image" | "text" | "avatar" | "card">;
3630
+ readonly type: _angular_core.InputSignal<OlSkeletonType>;
2753
3631
  /**
2754
3632
  * Chiều rộng của skeleton
2755
3633
  * Mặc định: 100% (hoặc theo preset của type)
2756
3634
  */
2757
- width: _angular_core.InputSignal<string | undefined>;
3635
+ readonly width: _angular_core.InputSignal<string | undefined>;
2758
3636
  /**
2759
3637
  * Chiều cao của skeleton
2760
3638
  * Mặc định: theo preset của type
2761
3639
  */
2762
- height: _angular_core.InputSignal<string | undefined>;
3640
+ readonly height: _angular_core.InputSignal<string | undefined>;
2763
3641
  /**
2764
3642
  * Bo tròn góc
2765
3643
  * - none: rounded-none
@@ -2769,20 +3647,20 @@ declare class OlSkeleton {
2769
3647
  * - xl: rounded-xl
2770
3648
  * - full: rounded-full (cho avatar)
2771
3649
  */
2772
- rounded: _angular_core.InputSignal<"sm" | "md" | "lg" | "xl" | "none" | "full">;
3650
+ readonly rounded: _angular_core.InputSignal<OlSkeletonRounded>;
2773
3651
  /**
2774
3652
  * Loại animation
2775
3653
  * - pulse: Nhấp nháy opacity (default)
2776
3654
  * - wave: Gradient di chuyển ngang
2777
3655
  * - none: Static không animation
2778
3656
  */
2779
- animation: _angular_core.InputSignal<"none" | "pulse" | "wave">;
3657
+ readonly animation: _angular_core.InputSignal<OlSkeletonAnimation>;
2780
3658
  /**
2781
3659
  * Aspect ratio cho image type
2782
3660
  * - 16/9: Landscape (default)
2783
3661
  * - 4/3: Standard
2784
3662
  */
2785
- aspectRatio: _angular_core.InputSignal<"16/9" | "4/3">;
3663
+ readonly aspectRatio: _angular_core.InputSignal<OlSkeletonAspectRatio>;
2786
3664
  /**
2787
3665
  * Computed classes cho skeleton
2788
3666
  */
@@ -2816,28 +3694,28 @@ interface SpeedDialAction {
2816
3694
 
2817
3695
  declare class OlSpeedDial {
2818
3696
  private readonly elementRef;
2819
- direction: _angular_core.InputSignal<SpeedDialDirection>;
2820
- type: _angular_core.InputSignal<SpeedDialType>;
2821
- trigger: _angular_core.InputSignal<SpeedDialTrigger>;
2822
- color: _angular_core.InputSignal<SpeedDialColor>;
2823
- icon: _angular_core.InputSignal<string>;
2824
- ariaLabel: _angular_core.InputSignal<string>;
2825
- tooltip: _angular_core.InputSignal<string | undefined>;
2826
- disabled: _angular_core.InputSignal<boolean>;
2827
- backdrop: _angular_core.InputSignal<boolean>;
2828
- closeOnClickOutside: _angular_core.InputSignal<boolean>;
2829
- closeOnEscape: _angular_core.InputSignal<boolean>;
2830
- actions: _angular_core.InputSignal<SpeedDialAction[]>;
2831
- customClass: _angular_core.InputSignal<string>;
2832
- isOpen: _angular_core.ModelSignal<boolean>;
2833
- onOpen: _angular_core.OutputEmitterRef<void>;
2834
- onClose: _angular_core.OutputEmitterRef<void>;
2835
- actionClick: _angular_core.OutputEmitterRef<SpeedDialAction>;
3697
+ readonly direction: _angular_core.InputSignal<SpeedDialDirection>;
3698
+ readonly type: _angular_core.InputSignal<SpeedDialType>;
3699
+ readonly trigger: _angular_core.InputSignal<SpeedDialTrigger>;
3700
+ readonly color: _angular_core.InputSignal<SpeedDialColor>;
3701
+ readonly icon: _angular_core.InputSignal<string>;
3702
+ readonly ariaLabel: _angular_core.InputSignal<string>;
3703
+ readonly tooltip: _angular_core.InputSignal<string | undefined>;
3704
+ readonly disabled: _angular_core.InputSignal<boolean>;
3705
+ readonly backdrop: _angular_core.InputSignal<boolean>;
3706
+ readonly closeOnClickOutside: _angular_core.InputSignal<boolean>;
3707
+ readonly closeOnEscape: _angular_core.InputSignal<boolean>;
3708
+ readonly actions: _angular_core.InputSignal<SpeedDialAction[]>;
3709
+ readonly customClass: _angular_core.InputSignal<string>;
3710
+ readonly isOpen: _angular_core.ModelSignal<boolean>;
3711
+ readonly onOpen: _angular_core.OutputEmitterRef<void>;
3712
+ readonly onClose: _angular_core.OutputEmitterRef<void>;
3713
+ readonly actionClick: _angular_core.OutputEmitterRef<SpeedDialAction>;
2836
3714
  private hoverTimeout?;
2837
3715
  containerClasses: _angular_core.Signal<string>;
2838
3716
  itemsContainerClasses: _angular_core.Signal<string>;
2839
3717
  triggerButtonClasses: _angular_core.Signal<string>;
2840
- tooltipPositionForItems: _angular_core.Signal<"left" | "top">;
3718
+ tooltipPositionForItems: _angular_core.Signal<"top" | "left">;
2841
3719
  toggle(): void;
2842
3720
  open(): void;
2843
3721
  close(): void;
@@ -2851,15 +3729,16 @@ declare class OlSpeedDial {
2851
3729
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlSpeedDial, "ol-speed-dial", never, { "direction": { "alias": "direction"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "trigger": { "alias": "trigger"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "tooltip": { "alias": "tooltip"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "backdrop": { "alias": "backdrop"; "required": false; "isSignal": true; }; "closeOnClickOutside": { "alias": "closeOnClickOutside"; "required": false; "isSignal": true; }; "closeOnEscape": { "alias": "closeOnEscape"; "required": false; "isSignal": true; }; "actions": { "alias": "actions"; "required": false; "isSignal": true; }; "customClass": { "alias": "class"; "required": false; "isSignal": true; }; "isOpen": { "alias": "isOpen"; "required": false; "isSignal": true; }; }, { "isOpen": "isOpenChange"; "onOpen": "onOpen"; "onClose": "onClose"; "actionClick": "actionClick"; }, never, ["*"], true, never>;
2852
3730
  }
2853
3731
 
3732
+ type OlSpeedDialTooltipPosition = 'top' | 'bottom' | 'left' | 'right';
2854
3733
  declare class OlSpeedDialItem {
2855
- label: _angular_core.InputSignal<string>;
2856
- icon: _angular_core.InputSignal<string | undefined>;
2857
- tooltip: _angular_core.InputSignal<string | undefined>;
2858
- tooltipPosition: _angular_core.InputSignal<"left" | "right" | "top" | "bottom">;
2859
- disabled: _angular_core.InputSignal<boolean>;
2860
- type: _angular_core.InputSignal<SpeedDialType>;
2861
- customClass: _angular_core.InputSignal<string>;
2862
- onClick: _angular_core.OutputEmitterRef<MouseEvent>;
3734
+ readonly label: _angular_core.InputSignal<string>;
3735
+ readonly icon: _angular_core.InputSignal<string | undefined>;
3736
+ readonly tooltip: _angular_core.InputSignal<string | undefined>;
3737
+ readonly tooltipPosition: _angular_core.InputSignal<OlSpeedDialTooltipPosition>;
3738
+ readonly disabled: _angular_core.InputSignal<boolean>;
3739
+ readonly type: _angular_core.InputSignal<SpeedDialType>;
3740
+ readonly customClass: _angular_core.InputSignal<string>;
3741
+ readonly onClick: _angular_core.OutputEmitterRef<MouseEvent>;
2863
3742
  handleClick(event: MouseEvent): void;
2864
3743
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlSpeedDialItem, never>;
2865
3744
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlSpeedDialItem, "ol-speed-dial-item", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "tooltip": { "alias": "tooltip"; "required": false; "isSignal": true; }; "tooltipPosition": { "alias": "tooltipPosition"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "customClass": { "alias": "class"; "required": false; "isSignal": true; }; }, { "onClick": "onClick"; }, never, ["*"], true, never>;
@@ -2880,6 +3759,9 @@ declare class OlSpeedDialItem {
2880
3759
  * // Pulse spinner
2881
3760
  * <ol-spinner variant="pulse" size="xl" color="success" />
2882
3761
  */
3762
+ type OlSpinnerVariant = 'circle' | 'dots' | 'pulse';
3763
+ type OlSpinnerSize = 'xs' | 'sm' | 'base' | 'lg' | 'xl';
3764
+ type OlSpinnerColor = 'brand' | 'white' | 'success' | 'warning' | 'danger' | 'gray';
2883
3765
  declare class OlSpinner {
2884
3766
  /**
2885
3767
  * Variant của spinner
@@ -2887,7 +3769,7 @@ declare class OlSpinner {
2887
3769
  * - dots: 3 chấm tròn nhấp nhô
2888
3770
  * - pulse: Hình tròn nhấp nháy scale
2889
3771
  */
2890
- variant: _angular_core.InputSignal<"circle" | "pulse" | "dots">;
3772
+ readonly variant: _angular_core.InputSignal<OlSpinnerVariant>;
2891
3773
  /**
2892
3774
  * Kích thước spinner
2893
3775
  * - xs: 12px (w-3 h-3)
@@ -2896,7 +3778,7 @@ declare class OlSpinner {
2896
3778
  * - lg: 36px (w-9 h-9)
2897
3779
  * - xl: 48px (w-12 h-12)
2898
3780
  */
2899
- size: _angular_core.InputSignal<"xs" | "sm" | "md" | "lg" | "xl">;
3781
+ readonly size: _angular_core.InputSignal<OlSpinnerSize>;
2900
3782
  /**
2901
3783
  * Màu sắc của spinner.
2902
3784
  *
@@ -2913,11 +3795,11 @@ declare class OlSpinner {
2913
3795
  * - danger: border-rose-600 (ngoài Figma)
2914
3796
  * - gray: border-gray-400 (ngoài Figma)
2915
3797
  */
2916
- color: _angular_core.InputSignal<"danger" | "brand" | "success" | "warning" | "white" | "gray">;
3798
+ readonly color: _angular_core.InputSignal<OlSpinnerColor>;
2917
3799
  /**
2918
3800
  * Aria label cho accessibility
2919
3801
  */
2920
- ariaLabel: _angular_core.InputSignal<string>;
3802
+ readonly ariaLabel: _angular_core.InputSignal<string>;
2921
3803
  /**
2922
3804
  * Computed classes cho spinner container
2923
3805
  */
@@ -2964,6 +3846,81 @@ declare class OlTable {
2964
3846
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTable, "ol-table", never, { "striped": { "alias": "striped"; "required": false; "isSignal": true; }; "stripedColumns": { "alias": "stripedColumns"; "required": false; "isSignal": true; }; "hoverable": { "alias": "hoverable"; "required": false; "isSignal": true; }; "caption": { "alias": "caption"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
2965
3847
  }
2966
3848
 
3849
+ /**
3850
+ * Một hàng dữ liệu trong `ol-table`, mang được 5 trạng thái của Figma
3851
+ * `ol-table-row` (trục `Trạng thái`, đọc 2026-09-16):
3852
+ *
3853
+ * Mặc định nền `bg-neutral-primary` · viền dưới `border-default-subtle`
3854
+ * Sọc nền `bg-neutral-secondary-medium` (#f9fafb)
3855
+ * Di chuột nền `bg-neutral-tertiary` (#f3f4f6)
3856
+ * Đã chọn nền `bg-brand-softer` (#f5f8ff)
3857
+ * Vô hiệu nền như Mặc định, opacity 0,55
3858
+ *
3859
+ * `ol-table` vẫn là VỎ BỌC — người dùng tự viết `<thead>`/`<tbody>`/`<td>`.
3860
+ * Component này chỉ thay chỗ của `<tr>`, không phải data-grid.
3861
+ *
3862
+ * HAI BẪY KỸ THUẬT đã đo trên trình duyệt thật (2026-09-16):
3863
+ *
3864
+ * 1. **Parser HTML đá thẻ lạ ra khỏi bảng.** Chỉ `<tr>` được nằm trong
3865
+ * `<tbody>`; gán chuỗi qua `innerHTML` thì `<ol-table-row>` bị "foster
3866
+ * parenting" đẩy ra ngoài bảng và hàng biến mất. Angular KHÔNG dính — nó
3867
+ * dựng DOM bằng `createElement`/`appendChild`, không qua parser, đo lại
3868
+ * `parentElement` vẫn là `tbody`. Nhưng ai copy mã mẫu vào một file `.html`
3869
+ * TĨNH sẽ thấy hàng mất, nên docs phải ghi rõ.
3870
+ *
3871
+ * 2. **Thẻ tuỳ biến mặc định `display: inline` làm ô co sập.** Đo bảng 300px,
3872
+ * 3 ô: mặc định ra 12·11·13px cao 20; `table-row` ra 99·95·105px cao 25.
3873
+ * `contents` cũng cho ô đúng nhưng host cao 0 — không có hộp để tô nền, mà
3874
+ * 4/5 trạng thái đều cần tô nền lên chính hàng. Vì vậy host BẮT BUỘC
3875
+ * `display: table-row`. Ràng buộc này vô hình: build xanh, chỉ đo DOM mới
3876
+ * thấy — đã khoá bằng spec.
3877
+ */
3878
+ declare class OlTableRow {
3879
+ /** Hàng đang được chọn — Figma `Trạng thái=Đã chọn`. */
3880
+ readonly selected: _angular_core.InputSignal<boolean>;
3881
+ /** Hàng bị vô hiệu — Figma `Trạng thái=Vô hiệu`, opacity 0,55. */
3882
+ readonly disabled: _angular_core.InputSignal<boolean>;
3883
+ /** Hàng nằm ở vị trí sọc — Figma `Trạng thái=Sọc`. */
3884
+ readonly striped: _angular_core.InputSignal<boolean>;
3885
+ /** Bật nền khi rê chuột — Figma `Trạng thái=Di chuột`. */
3886
+ readonly hoverable: _angular_core.InputSignal<boolean>;
3887
+ protected readonly rowClasses: _angular_core.Signal<string>;
3888
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlTableRow, never>;
3889
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTableRow, "ol-table-row", never, { "selected": { "alias": "selected"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "striped": { "alias": "striped"; "required": false; "isSignal": true; }; "hoverable": { "alias": "hoverable"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
3890
+ }
3891
+
3892
+ /**
3893
+ * Khung chờ của bảng — hiện trong lúc dữ liệu đang về.
3894
+ *
3895
+ * Figma `ol-table-loading` (trang ⤷ Tables, đọc 2026-09-16) ghi rõ mục đích:
3896
+ * *"giữ đúng số cột và chiều cao dòng của bảng thật để không giật bố cục khi
3897
+ * dữ liệu về. Không có dữ liệu thì chuyển sang Bảng rỗng."*
3898
+ *
3899
+ * Số đo lấy từ Figma: đầu bảng cao 38px nền `bg-brand-softer` (#f5f8ff), mỗi
3900
+ * dòng cao 40px nền trắng, viền dưới `border-default-subtle`.
3901
+ *
3902
+ * Đây là MẢNH GHÉP RỜI, không phải trạng thái bên trong `ol-table`: người dùng
3903
+ * tự quyết định hiện nó hay hiện bảng thật, đúng lối "khung + tự đặt `<tr>`".
3904
+ */
3905
+ declare class OlTableLoading {
3906
+ /** Số cột — phải khớp bảng thật, nếu không bố cục sẽ giật khi dữ liệu về. */
3907
+ readonly columns: _angular_core.InputSignal<number>;
3908
+ /** Số dòng giả. Figma vẽ 5. */
3909
+ readonly rows: _angular_core.InputSignal<number>;
3910
+ /** Vẽ cả hàng tiêu đề giả. */
3911
+ readonly showHeader: _angular_core.InputSignal<boolean>;
3912
+ readonly ariaLabel: _angular_core.InputSignal<string>;
3913
+ /**
3914
+ * `Array(n)` ném `Invalid array length` với số âm hoặc lẻ, và treo máy với
3915
+ * số rất lớn — cùng họ lỗi BUG-010. Kẹp về khoảng dùng được trước khi dựng.
3916
+ */
3917
+ private readonly sach;
3918
+ protected readonly colList: _angular_core.Signal<number[]>;
3919
+ protected readonly rowList: _angular_core.Signal<number[]>;
3920
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlTableLoading, never>;
3921
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTableLoading, "ol-table-loading", never, { "columns": { "alias": "columns"; "required": false; "isSignal": true; }; "rows": { "alias": "rows"; "required": false; "isSignal": true; }; "showHeader": { "alias": "showHeader"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3922
+ }
3923
+
2967
3924
  declare class OlTabItem {
2968
3925
  readonly tabId: _angular_core.InputSignal<string>;
2969
3926
  readonly label: _angular_core.InputSignal<string>;
@@ -2984,8 +3941,25 @@ declare class OlTabItem {
2984
3941
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTabItem, "ol-tab-item", never, { "tabId": { "alias": "tabId"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "helper": { "alias": "helper"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "avatar": { "alias": "avatar"; "required": false; "isSignal": true; }; "badge": { "alias": "badge"; "required": false; "isSignal": true; }; "badgeTheme": { "alias": "badgeTheme"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
2985
3942
  }
2986
3943
 
2987
- type TabsVariant = 'default' | 'underline' | 'border-bottom' | 'pills' | 'button' | 'boxed' | 'button-group' | 'vertical' | 'vertical-pills' | 'vertical-list' | 'card';
2988
- type TabsSize = 'sm' | 'md' | 'lg';
3944
+ /**
3945
+ * Kiểu hiển thị nhóm tab.
3946
+ *
3947
+ * Figma `ol-tabs` trục Type có 7 giá trị (đọc 2026-09-16):
3948
+ * Default · Pills · Button · Button group · Border bottom ·
3949
+ * Vertical with list item · Vertical pills
3950
+ *
3951
+ * `boxed` và `vertical` là biến thể code THÊM — ngoài Figma nhưng CÓ nhánh
3952
+ * xử lý thật, giữ lại.
3953
+ *
3954
+ * `underline` là BÍ DANH của `border-bottom`: template đã xử lý nó ở 3 chỗ
3955
+ * (`variant() === 'border-bottom' || variant() === 'underline'`) mà union
3956
+ * lại thiếu — truyền vào là build đỏ dù mã chạy đúng. Nay khai đủ.
3957
+ *
3958
+ * `card` đã GỠ: chỉ khai trong union, KHÔNG nhánh nào xử lý. Đo DOM
3959
+ * (/tabs, 2026-09-16): `variant="card"` render class y hệt `default`.
3960
+ */
3961
+ type TabsVariant = 'default' | 'underline' | 'border-bottom' | 'pills' | 'button' | 'boxed' | 'button-group' | 'vertical' | 'vertical-pills' | 'vertical-list';
3962
+ type TabsSize = 'sm' | 'base' | 'lg';
2989
3963
  declare class OlTabs {
2990
3964
  readonly variant: _angular_core.InputSignal<TabsVariant>;
2991
3965
  readonly size: _angular_core.InputSignal<TabsSize>;
@@ -3000,6 +3974,21 @@ declare class OlTabs {
3000
3974
  title: string;
3001
3975
  }>;
3002
3976
  readonly items: _angular_core.Signal<readonly OlTabItem[]>;
3977
+ /**
3978
+ * Tab đang xem — gộp `selectedIndex` và bí danh `activeTab`.
3979
+ *
3980
+ * Bản cũ kiểm `typeof sel === 'number'` rồi trả luôn. Nhưng
3981
+ * `selectedIndex` khai `model<number>(0)` nên điều kiện đó LUÔN đúng —
3982
+ * `activeTab` không bao giờ được đọc. Hệ quả: đặt `activeTab = 2` ra tab 0,
3983
+ * và đường chọn theo ID (`activeTab = 'tab-c'`) — tính năng RIÊNG của bí
3984
+ * danh này — chưa bao giờ chạy.
3985
+ *
3986
+ * Cùng lỗi với `currentPage` của `ol-pagination`: alias công khai mà vô
3987
+ * dụng vì vế trái luôn có giá trị thật.
3988
+ *
3989
+ * Nay so với MẶC ĐỊNH (0): cái nào khác 0 thì đó là ý người dùng.
3990
+ * `selectTab` đặt cả hai nên sau lần bấm đầu chúng luôn bằng nhau.
3991
+ */
3003
3992
  readonly currentIndex: _angular_core.Signal<number>;
3004
3993
  readonly currentItem: _angular_core.Signal<OlTabItem>;
3005
3994
  readonly isMobileDropdown: _angular_core.Signal<boolean>;
@@ -3073,7 +4062,7 @@ declare class OlToastService {
3073
4062
  }
3074
4063
 
3075
4064
  declare class OlToast implements OnInit, OnDestroy {
3076
- config: _angular_core.InputSignal<Toast>;
4065
+ readonly config: _angular_core.InputSignal<Toast>;
3077
4066
  /**
3078
4067
  * Bậc thẻ tiêu đề (`<h1>`..`<h6>`). Mặc định **4** để giữ nguyên hành vi cũ.
3079
4068
  *
@@ -3081,7 +4070,7 @@ declare class OlToast implements OnInit, OnDestroy {
3081
4070
  * nhảy bậc và nơi gọi không sửa được.
3082
4071
  */
3083
4072
  readonly headingLevel: _angular_core.InputSignal<1 | 2 | 3 | 4 | 5 | 6>;
3084
- dismiss: _angular_core.OutputEmitterRef<void>;
4073
+ readonly dismiss: _angular_core.OutputEmitterRef<void>;
3085
4074
  progress: _angular_core.WritableSignal<number>;
3086
4075
  private progressInterval?;
3087
4076
  private isPaused;
@@ -3110,6 +4099,40 @@ declare class OlToastContainer {
3110
4099
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlToastContainer, "ol-toast-container", never, {}, {}, never, never, true, never>;
3111
4100
  }
3112
4101
 
4102
+ /**
4103
+ * Internal Tooltip Component
4104
+ * Renders the actual tooltip content with theme and arrow support
4105
+ * Used internally by OlTooltipDirective
4106
+ */
4107
+ /**
4108
+ * Tông màu tooltip. Khai ở ĐÂY và `OlTooltipDirective` dùng lại — hai nơi
4109
+ * cùng một union, tách thành hai kiểu riêng là để người dùng phải nhớ chúng
4110
+ * bằng nhau.
4111
+ */
4112
+ type OlTooltipTheme = 'dark' | 'light' | 'brand';
4113
+ /** Hướng hiện tooltip so với phần tử đích. */
4114
+ type OlTooltipPosition = 'top' | 'bottom' | 'left' | 'right';
4115
+ declare class OlTooltip {
4116
+ /** Tooltip content text */
4117
+ readonly content: _angular_core.InputSignal<string>;
4118
+ /** Theme variant */
4119
+ readonly theme: _angular_core.InputSignal<OlTooltipTheme>;
4120
+ /** Tooltip ID for ARIA */
4121
+ readonly tooltipId: _angular_core.InputSignal<string>;
4122
+ /** Show arrow */
4123
+ readonly arrow: _angular_core.InputSignal<boolean>;
4124
+ /**
4125
+ * Computed tooltip classes based on theme
4126
+ */
4127
+ tooltipClasses: _angular_core.Signal<string>;
4128
+ /**
4129
+ * Computed arrow classes based on theme
4130
+ */
4131
+ arrowClasses: _angular_core.Signal<string>;
4132
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlTooltip, never>;
4133
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTooltip, "ol-tooltip-internal", never, { "content": { "alias": "content"; "required": true; "isSignal": true; }; "theme": { "alias": "theme"; "required": false; "isSignal": true; }; "tooltipId": { "alias": "tooltipId"; "required": false; "isSignal": true; }; "arrow": { "alias": "arrow"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
4134
+ }
4135
+
3113
4136
  /**
3114
4137
  * Tooltip Directive
3115
4138
  * Provides accessible tooltips with Floating UI positioning
@@ -3132,17 +4155,17 @@ declare class OlTooltipDirective implements OnDestroy {
3132
4155
  private readonly injector;
3133
4156
  private readonly positioningService;
3134
4157
  /** Tooltip content */
3135
- olTooltip: _angular_core.InputSignal<string>;
4158
+ readonly olTooltip: _angular_core.InputSignal<string>;
3136
4159
  /** Tooltip position */
3137
- tooltipPosition: _angular_core.InputSignal<"left" | "right" | "top" | "bottom">;
4160
+ readonly tooltipPosition: _angular_core.InputSignal<OlTooltipPosition>;
3138
4161
  /** Tooltip theme */
3139
- tooltipTheme: _angular_core.InputSignal<"brand" | "dark" | "light">;
4162
+ readonly tooltipTheme: _angular_core.InputSignal<OlTooltipTheme>;
3140
4163
  /** Delay before showing tooltip (ms) */
3141
- tooltipDelay: _angular_core.InputSignal<number>;
4164
+ readonly tooltipDelay: _angular_core.InputSignal<number>;
3142
4165
  /** Disable tooltip */
3143
- tooltipDisabled: _angular_core.InputSignal<boolean>;
4166
+ readonly tooltipDisabled: _angular_core.InputSignal<boolean>;
3144
4167
  /** Show arrow */
3145
- tooltipArrow: _angular_core.InputSignal<boolean>;
4168
+ readonly tooltipArrow: _angular_core.InputSignal<boolean>;
3146
4169
  private showTimeout?;
3147
4170
  private hideTimeout?;
3148
4171
  private touchTimeout?;
@@ -3198,33 +4221,7 @@ declare class OlTooltipDirective implements OnDestroy {
3198
4221
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<OlTooltipDirective, "[olTooltip]", never, { "olTooltip": { "alias": "olTooltip"; "required": true; "isSignal": true; }; "tooltipPosition": { "alias": "tooltipPosition"; "required": false; "isSignal": true; }; "tooltipTheme": { "alias": "tooltipTheme"; "required": false; "isSignal": true; }; "tooltipDelay": { "alias": "tooltipDelay"; "required": false; "isSignal": true; }; "tooltipDisabled": { "alias": "tooltipDisabled"; "required": false; "isSignal": true; }; "tooltipArrow": { "alias": "tooltipArrow"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3199
4222
  }
3200
4223
 
3201
- /**
3202
- * Internal Tooltip Component
3203
- * Renders the actual tooltip content with theme and arrow support
3204
- * Used internally by OlTooltipDirective
3205
- */
3206
- declare class OlTooltip {
3207
- /** Tooltip content text */
3208
- content: _angular_core.InputSignal<string>;
3209
- /** Theme variant */
3210
- theme: _angular_core.InputSignal<"brand" | "dark" | "light">;
3211
- /** Tooltip ID for ARIA */
3212
- tooltipId: _angular_core.InputSignal<string>;
3213
- /** Show arrow */
3214
- arrow: _angular_core.InputSignal<boolean>;
3215
- /**
3216
- * Computed tooltip classes based on theme
3217
- */
3218
- tooltipClasses: _angular_core.Signal<string>;
3219
- /**
3220
- * Computed arrow classes based on theme
3221
- */
3222
- arrowClasses: _angular_core.Signal<string>;
3223
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlTooltip, never>;
3224
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTooltip, "ol-tooltip-internal", never, { "content": { "alias": "content"; "required": true; "isSignal": true; }; "theme": { "alias": "theme"; "required": false; "isSignal": true; }; "tooltipId": { "alias": "tooltipId"; "required": false; "isSignal": true; }; "arrow": { "alias": "arrow"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3225
- }
3226
-
3227
- type IconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | number;
4224
+ type IconSize = 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl' | '3xl' | number;
3228
4225
  type IconColor = 'current' | 'brand' | 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'white' | 'muted' | string;
3229
4226
  type IconVariant = 'linear' | 'bold' | 'line-duotone' | 'file-type' | 'brand' | 'custom' | string;
3230
4227
  declare class OlIcon {
@@ -3295,11 +4292,12 @@ declare class OlIconService {
3295
4292
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<any>;
3296
4293
  }
3297
4294
 
4295
+ type OlBottomNavPosition = 'fixed' | 'relative';
3298
4296
  declare class OlBottomNav {
3299
4297
  readonly bordered: _angular_core.InputSignal<boolean>;
3300
4298
  readonly shadow: _angular_core.InputSignal<boolean>;
3301
4299
  readonly safeArea: _angular_core.InputSignal<boolean>;
3302
- readonly position: _angular_core.InputSignal<"relative" | "fixed">;
4300
+ readonly position: _angular_core.InputSignal<OlBottomNavPosition>;
3303
4301
  readonly navClasses: _angular_core.Signal<string>;
3304
4302
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlBottomNav, never>;
3305
4303
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlBottomNav, "ol-bottom-nav", never, { "bordered": { "alias": "bordered"; "required": false; "isSignal": true; }; "shadow": { "alias": "shadow"; "required": false; "isSignal": true; }; "safeArea": { "alias": "safeArea"; "required": false; "isSignal": true; }; "position": { "alias": "position"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
@@ -3378,17 +4376,44 @@ interface OlTreeNode {
3378
4376
  data?: any;
3379
4377
  }
3380
4378
 
4379
+ type OlTreeSelectionMode = 'single' | 'multiple';
4380
+ type OlTreeToggleIcon = 'chevron' | 'plus-minus';
3381
4381
  declare class OlTree {
3382
4382
  readonly nodes: _angular_core.ModelSignal<OlTreeNode[]>;
3383
4383
  readonly checkable: _angular_core.InputSignal<boolean>;
3384
- readonly selectionMode: _angular_core.InputSignal<"single" | "multiple">;
4384
+ readonly selectionMode: _angular_core.InputSignal<OlTreeSelectionMode>;
3385
4385
  readonly bordered: _angular_core.InputSignal<boolean>;
3386
- readonly toggleIcon: _angular_core.InputSignal<"plus-minus" | "chevron">;
4386
+ readonly toggleIcon: _angular_core.InputSignal<OlTreeToggleIcon>;
3387
4387
  readonly nodeSelect: _angular_core.OutputEmitterRef<OlTreeNode>;
3388
4388
  readonly nodeCheck: _angular_core.OutputEmitterRef<{
3389
4389
  node: OlTreeNode;
3390
4390
  checked: boolean;
3391
4391
  }>;
4392
+ /**
4393
+ * Node đang giữ điểm dừng Tab — ROVING TABINDEX theo chuẩn WAI-ARIA Tree
4394
+ * View: đúng MỘT node mang `tabindex="0"`, mọi node còn lại `-1`.
4395
+ *
4396
+ * Trước đây mọi node đều `tabindex="0"`: cây 200 node thì người dùng bàn
4397
+ * phím phải Tab 200 lần mới qua được. Figma ghi thẳng trong phần lưu ý —
4398
+ * "Focus dùng viền 2px border-focus, BẮT BUỘC cho điều hướng bằng bàn phím"
4399
+ * — nhưng mã không có một `keydown` nào (đo 2026-09-16).
4400
+ *
4401
+ * `null` nghĩa là chưa ai focus: khi đó node ĐẦU TIÊN giữ điểm dừng.
4402
+ */
4403
+ readonly focusedId: _angular_core.WritableSignal<string | null>;
4404
+ /** Danh sách node ĐANG NHÌN THẤY, theo đúng thứ tự trên màn hình. */
4405
+ private readonly visibleNodes;
4406
+ /** Node nào thật sự giữ `tabindex="0"` lúc này. */
4407
+ readonly tabbableId: _angular_core.Signal<string | null>;
4408
+ /**
4409
+ * Điều hướng bàn phím theo WAI-ARIA Tree View.
4410
+ *
4411
+ * Không dùng `preventDefault` cho phím không nhận: Tab phải thoát khỏi cây
4412
+ * được, và chữ cái không khớp thì để nguyên cho trình duyệt.
4413
+ */
4414
+ handleKeydown(event: KeyboardEvent): void;
4415
+ /** Cha trực tiếp của một node, `null` nếu node ở mức gốc. */
4416
+ private timCha;
3392
4417
  handleNodeClick(node: OlTreeNode): void;
3393
4418
  handleNodeCheck(event: {
3394
4419
  node: OlTreeNode;
@@ -3404,18 +4429,34 @@ declare class OlTree {
3404
4429
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTree, "ol-tree", never, { "nodes": { "alias": "nodes"; "required": false; "isSignal": true; }; "checkable": { "alias": "checkable"; "required": false; "isSignal": true; }; "selectionMode": { "alias": "selectionMode"; "required": false; "isSignal": true; }; "bordered": { "alias": "bordered"; "required": false; "isSignal": true; }; "toggleIcon": { "alias": "toggleIcon"; "required": false; "isSignal": true; }; }, { "nodes": "nodesChange"; "nodeSelect": "nodeSelect"; "nodeCheck": "nodeCheck"; }, never, never, true, never>;
3405
4430
  }
3406
4431
 
4432
+ type OlTreeNodeSelectionMode = 'single' | 'multiple';
4433
+ type OlTreeNodeToggleIcon = 'chevron' | 'plus-minus';
3407
4434
  declare class OlTreeNodeItem {
3408
4435
  readonly node: _angular_core.InputSignal<OlTreeNode>;
3409
4436
  readonly checkable: _angular_core.InputSignal<boolean>;
3410
- readonly selectionMode: _angular_core.InputSignal<"single" | "multiple">;
3411
- readonly toggleIcon: _angular_core.InputSignal<"plus-minus" | "chevron">;
4437
+ readonly selectionMode: _angular_core.InputSignal<OlTreeNodeSelectionMode>;
4438
+ readonly toggleIcon: _angular_core.InputSignal<OlTreeNodeToggleIcon>;
3412
4439
  readonly level: _angular_core.InputSignal<number>;
4440
+ /** Node nào giữ điểm dừng Tab — xem `focusedId` trong `ol-tree`. */
4441
+ readonly tabbableId: _angular_core.InputSignal<string | null>;
4442
+ /** Node nào đang được điều hướng tới; dùng để gọi `.focus()` thật. */
4443
+ readonly focusedId: _angular_core.InputSignal<string | null>;
3413
4444
  readonly nodeClick: _angular_core.OutputEmitterRef<OlTreeNode>;
3414
4445
  readonly nodeCheck: _angular_core.OutputEmitterRef<{
3415
4446
  node: OlTreeNode;
3416
4447
  checked: boolean;
3417
4448
  }>;
3418
4449
  readonly toggleExpand: _angular_core.OutputEmitterRef<OlTreeNode>;
4450
+ /** Người dùng bấm hoặc Tab vào node này — vỏ cây ghi lại làm điểm dừng. */
4451
+ readonly nodeFocus: _angular_core.OutputEmitterRef<string>;
4452
+ private readonly hang;
4453
+ /** Roving tabindex: đúng một node trong cả cây mang `0`. */
4454
+ protected readonly tabIndex: _angular_core.Signal<0 | -1>;
4455
+ /** Mức sâu cho `aria-level` — WAI-ARIA đếm từ 1, `level` đếm từ 0. */
4456
+ protected readonly ariaLevel: _angular_core.Signal<number>;
4457
+ /** `aria-expanded` CHỈ đặt cho node có con; node lá để `null`. */
4458
+ protected readonly ariaExpanded: _angular_core.Signal<boolean | null>;
4459
+ constructor();
3419
4460
  /**
3420
4461
  * Nút cha có con chọn một phần thì ô của nó phải là dấu gạch.
3421
4462
  *
@@ -3430,12 +4471,12 @@ declare class OlTreeNodeItem {
3430
4471
  * Đếm ĐỆ QUY, không chỉ con trực tiếp: cháu chọn mà con chưa, thì con vẫn
3431
4472
  * phải là gạch chứ không phải trống.
3432
4473
  */
3433
- readonly motPhan: _angular_core.Signal<boolean>;
4474
+ readonly indeterminate: _angular_core.Signal<boolean>;
3434
4475
  handleToggle(event: MouseEvent): void;
3435
4476
  handleClick(): void;
3436
4477
  handleCheck(checked: boolean): void;
3437
4478
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlTreeNodeItem, never>;
3438
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTreeNodeItem, "ol-tree-node", never, { "node": { "alias": "node"; "required": true; "isSignal": true; }; "checkable": { "alias": "checkable"; "required": false; "isSignal": true; }; "selectionMode": { "alias": "selectionMode"; "required": false; "isSignal": true; }; "toggleIcon": { "alias": "toggleIcon"; "required": false; "isSignal": true; }; "level": { "alias": "level"; "required": false; "isSignal": true; }; }, { "nodeClick": "nodeClick"; "nodeCheck": "nodeCheck"; "toggleExpand": "toggleExpand"; }, never, never, true, never>;
4479
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTreeNodeItem, "ol-tree-node", never, { "node": { "alias": "node"; "required": true; "isSignal": true; }; "checkable": { "alias": "checkable"; "required": false; "isSignal": true; }; "selectionMode": { "alias": "selectionMode"; "required": false; "isSignal": true; }; "toggleIcon": { "alias": "toggleIcon"; "required": false; "isSignal": true; }; "level": { "alias": "level"; "required": false; "isSignal": true; }; "tabbableId": { "alias": "tabbableId"; "required": false; "isSignal": true; }; "focusedId": { "alias": "focusedId"; "required": false; "isSignal": true; }; }, { "nodeClick": "nodeClick"; "nodeCheck": "nodeCheck"; "toggleExpand": "toggleExpand"; "nodeFocus": "nodeFocus"; }, never, never, true, never>;
3439
4480
  }
3440
4481
 
3441
4482
  type ChartType = 'line' | 'smooth-line' | 'area' | 'smooth-area' | 'step-line' | 'bar' | 'stacked-bar' | 'horizontal-bar' | 'stacked-horizontal-bar' | 'diverging-bar' | 'pie' | 'donut' | 'rose' | 'radar' | 'gauge' | 'radial' | 'scatter' | 'heatmap' | 'boxplot';
@@ -3869,19 +4910,21 @@ declare class OlDeviceMockup {
3869
4910
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDeviceMockup, "ol-device-mockup", never, { "device": { "alias": "device"; "required": false; "isSignal": true; }; "url": { "alias": "url"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
3870
4911
  }
3871
4912
 
4913
+ type OlGalleryGap = 'sm' | 'md' | 'lg';
3872
4914
  declare class OlGallery {
3873
4915
  readonly cols: _angular_core.InputSignal<2 | 3 | 4 | 5>;
3874
- readonly gap: _angular_core.InputSignal<"sm" | "md" | "lg">;
4916
+ readonly gap: _angular_core.InputSignal<OlGalleryGap>;
3875
4917
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlGallery, never>;
3876
4918
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlGallery, "ol-gallery", never, { "cols": { "alias": "cols"; "required": false; "isSignal": true; }; "gap": { "alias": "gap"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
3877
4919
  }
3878
4920
 
4921
+ type OlGalleryRatio = 'square' | 'video' | 'portrait' | 'auto';
3879
4922
  declare class OlGalleryItem {
3880
4923
  readonly src: _angular_core.InputSignal<string>;
3881
4924
  readonly alt: _angular_core.InputSignal<string>;
3882
4925
  readonly title: _angular_core.InputSignal<string>;
3883
4926
  readonly subtitle: _angular_core.InputSignal<string>;
3884
- readonly ratio: _angular_core.InputSignal<"video" | "square" | "auto" | "portrait">;
4927
+ readonly ratio: _angular_core.InputSignal<OlGalleryRatio>;
3885
4928
  readonly itemClick: _angular_core.OutputEmitterRef<void>;
3886
4929
  handleClick(): void;
3887
4930
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlGalleryItem, never>;
@@ -4133,6 +5176,60 @@ declare class OlPositioningService {
4133
5176
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<any>;
4134
5177
  }
4135
5178
 
4136
- export { CHAT_BUBBLE_STATUS, MONTHS_FULL_EN, MONTHS_FULL_VI, MONTHS_SHORT_VI, OlAccordion, OlAccordionItem, OlAlert, OlAlertDialog, OlAspectRatio, OlAutocomplete, OlAvatar, OlAvatarGroup, OlBackdrop, OlBadge, OlBanner, OlBottomNav, OlBottomNavItem, OlBreadcrumb, OlBreadcrumbItem, OlButton, OlButtonGroup, OlCard, OlCarousel, OlCarouselSlide, OlChart, OlChatBubble, OlChatContainer, OlCheckbox, OlCheckboxRadioForm, OlCheckboxRadioGroup, OlClipboard, OlCollapse, OlCommand, OlDateRangePicker, OlDatepicker, OlDatepickerDirective, OlDatepickerFooter, OlDatepickerHeader, OlDatepickerPopup, OlDatepickerStateService, OlDayView, OlDeviceMockup, OlDownloadAppButton, OlDrawer, OlDrawerFooter, OlDrawerHeader, OlDropdown, OlDropdownHeader, OlDropdownItem, OlEmpty, OlFileInput, OlFloatingLabel, OlFocusTrapService, OlFooter, OlGallery, OlGalleryItem, OlHelper, OlIcon, OlIconService, OlIconShape, OlInput, OlInputCounter, OlJumbotron, OlKbd, OlLabel, OlListGroup, OlListGroupItem, OlModal, OlModalFooter, OlModalHeader, OlMonthView, OlNavDropdown, OlNavbar, OlNavbarItem, OlOtpInput, OlOverlayBase, OlPagination, OlPopover, OlPopoverContent, OlPositioningService, OlProgress, OlRadio, OlRange, OlRating, OlRichTextEditor, OlScrollArea, OlSelect, OlSeparator, OlSidebar, OlSidebarGroup, OlSidebarItem, OlSidebarSubmenu, OlSkeleton, OlSpeedDial, OlSpeedDialItem, OlSpinner, OlStepItem, OlStepper, OlTabItem, OlTable, OlTabs, OlTagInput, OlTextarea, OlTimeline, OlTimelineItem, OlTimepicker, OlToast, OlToastContainer, OlToastService, OlToggle, OlToggleGroup, OlToggleInput, OlTooltip, OlTooltipDirective, OlTree, OlTreeNodeItem, OlYearView, WEEKDAYS_SHORT_EN, WEEKDAYS_SHORT_VI, boDau, cloneDate, endOfDay, formatDate, generateMonthMatrix, generateMonthsMatrix, generateYearsMatrix, isBetween, isDateDisabled, isSameDay, isSameMonth, isSameYear, isToday, parseDate, startOfDay };
4137
- export type { AccordionVariant, AlertColor, AlertDialogTone, AlertLayout, AlertRounded, AlertType, AlertVariant, AppDevice, AppTheme, AutocompleteOption, AutocompleteSize, AutocompleteState, AvatarShape, AvatarSize, AvatarStatus, BadgeShape, BadgeSize, BadgeTheme, BadgeType, BannerPosition, BannerVariant, BoxplotDataPoint, BreadcrumbItemStatus, BreadcrumbSeparator, BreadcrumbVariant, ButtonColor, ButtonGroupColor, ButtonGroupOrientation, ButtonGroupSize, ButtonSize, CardPadding, CardRadius, CardShadow, CardVariant, ChartDataPoint, ChartSeries, ChartType, ChatBubbleStatus, ChatSender, CheckboxSize, ChoiceControl, ChoiceControlPosition, ChoiceGroupLayout, ChoiceLayout, ChoiceShape, CommandGroup, CommandItem, DateCustomClass, DateRange, DatepickerPreset, DatepickerSelectionMode, DatepickerSize, DatepickerViewMode, DayCell, DeviceType, DrawerFooterLayout, DrawerPlacement, DrawerSize, DropdownAutoClose, DropdownPlacement, DropdownTrigger, EmptySize, FileInputSize, FloatingLabelSize, FloatingLabelState, FloatingLabelVariant, HeatmapData, HelperFeature, HelperRule, HelperSize, HelperState, IconColor, IconShapeColor, IconShapeSize, IconShapeType, IconSize, IconVariant, InputCounterSize, InputSize, InputState, JumbotronVariant, ListGroupType, ModalFooterLayout, ModalPlacement, ModalSize, MonthCell, OlTreeNode, OtpInputSize, PaginationType, PaginationVariant, PopoverPlacement, PopoverTrigger, PositionedCoordinates, PositioningOptions, ProgressColor, ProgressLabelPosition, ProgressSize, RadarIndicator, RadioSize, RatingType, ScatterDataPoint, ScatterSeries, ScrollAreaAxis, ScrollAreaScrollbar, SelectOption, SelectSize, SelectState, SeparatorOrientation, SpeedDialAction, SpeedDialColor, SpeedDialDirection, SpeedDialTrigger, SpeedDialType, StepState, StepperBreakpoint, StepperOrientation, StepperVariant, TabsSize, TabsVariant, TagInputSize, TextareaState, TimeObject, TimepickerFormat, TimepickerSize, TimepickerState, Toast, ToastConfig, ToastPosition, ToggleColor, TogglePosition, ToggleSize, YearCell };
5179
+ /**
5180
+ * Container CHUNG cho mọi lớp nổi, gắn thẳng vào `<body>`.
5181
+ *
5182
+ * VÌ SAO CẦN — đo trên `/modal` ngày 2026-09-17, dropdown đặt trong modal hỏng
5183
+ * vì HAI lý do chồng nhau, và z-index chỉ chữa được một:
5184
+ *
5185
+ * 1. Khung modal có `z-index: 80` + `backdrop-filter: blur(4px)` nên nó TẠO
5186
+ * STACKING CONTEXT. Dropdown nằm bên trong chỉ xếp lớp so với anh em
5187
+ * CÙNG trong modal — đặt `z-index: 9999` cũng không thoát ra được.
5188
+ * 2. Hộp thoại có `overflow: hidden` (giữ bo góc) nên phần dropdown tràn ra
5189
+ * bị CẮT THẬT. Không z-index nào chữa được điều này.
5190
+ *
5191
+ * CÁCH BA THƯ VIỆN LỚN LÀM (đọc mã tải từ npm, không nói từ trí nhớ):
5192
+ *
5193
+ * · Angular CDK 22.1.7 — `document.body` (20 lượt gọi), một container chung
5194
+ * `cdk-overlay-container`, `z-index: 1000`.
5195
+ * · PrimeNG 22.1.1 — `appendTo` + `ZIndexUtils.generateZIndex` cấp z-index
5196
+ * ĐỘNG theo ngăn xếp (menu 1000 · modal 1100), không phải số cố định.
5197
+ * · ng-select 24 — `appendTo` + CDK Overlay, tức dùng lại cách của CDK.
5198
+ *
5199
+ * Cả ba cùng một nguyên lý: ĐẨY LỚP NỔI RA `<body>`. Không ai cố chữa bằng
5200
+ * z-index đơn thuần.
5201
+ *
5202
+ * Ở đây lấy container chung của CDK cộng z-index động của PrimeNG — thang tĩnh
5203
+ * không xử lý được ca modal chồng modal, mỗi lớp mở sau phải nằm trên lớp
5204
+ * trước.
5205
+ */
5206
+ declare class OlOverlayContainer {
5207
+ private readonly doc;
5208
+ private host;
5209
+ /**
5210
+ * Bộ đếm cấp z-index tăng dần.
5211
+ *
5212
+ * Mốc 1000 nằm TRÊN toàn bộ thang token của olkit (cao nhất là
5213
+ * `--z-toast: 100`), nên lớp nổi trong container luôn phủ mọi thứ vẽ theo
5214
+ * thang đó. Cùng mốc CDK và PrimeNG dùng.
5215
+ */
5216
+ private zHienTai;
5217
+ /** Container chung, tạo một lần rồi dùng lại. */
5218
+ getContainer(): HTMLElement;
5219
+ /**
5220
+ * Cấp z-index cho một lớp vừa mở. Mỗi lần gọi trả số LỚN HƠN lần trước —
5221
+ * lớp mở sau luôn nằm trên, kể cả khi mở modal trong modal.
5222
+ */
5223
+ capZIndex(): number;
5224
+ /**
5225
+ * Chuyển một phần tử vào container và cấp z-index.
5226
+ * Trả về hàm hoàn tác — gọi khi lớp đóng để trả phần tử về chỗ cũ.
5227
+ */
5228
+ gan(el: HTMLElement): () => void;
5229
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlOverlayContainer, never>;
5230
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<any>;
5231
+ }
5232
+
5233
+ export { CHAT_BUBBLE_STATUS, MONTHS_FULL_EN, MONTHS_FULL_VI, MONTHS_SHORT_VI, OlAccordion, OlAccordionItem, OlAlert, OlAlertDialog, OlAspectRatio, OlAutocomplete, OlAvatar, OlAvatarGroup, OlBackdrop, OlBadge, OlBanner, OlBottomNav, OlBottomNavItem, OlBreadcrumb, OlBreadcrumbItem, OlButton, OlButtonGroup, OlCard, OlCarousel, OlCarouselSlide, OlChart, OlChatBubble, OlChatContainer, OlCheckbox, OlCheckboxRadioForm, OlCheckboxRadioGroup, OlClipboard, OlCollapse, OlCommand, OlDateRangePicker, OlDatepicker, OlDatepickerDirective, OlDatepickerFooter, OlDatepickerHeader, OlDatepickerPopup, OlDatepickerStateService, OlDayView, OlDeviceMockup, OlDownloadAppButton, OlDrawer, OlDrawerFooter, OlDrawerHeader, OlDropdown, OlDropdownHeader, OlDropdownItem, OlEmpty, OlFileInput, OlFloatingLabel, OlFocusTrapService, OlFooter, OlGallery, OlGalleryItem, OlHelper, OlIcon, OlIconService, OlIconShape, OlInput, OlInputCounter, OlJumbotron, OlKbd, OlLabel, OlListGroup, OlListGroupItem, OlModal, OlModalFooter, OlModalHeader, OlMonthView, OlNavDropdown, OlNavbar, OlNavbarItem, OlOtpInput, OlOverlayBase, OlOverlayContainer, OlPagination, OlPopover, OlPopoverContent, OlPositioningService, OlProgress, OlRadio, OlRange, OlRating, OlRichTextEditor, OlScrollArea, OlSelect, OlSeparator, OlSidebar, OlSidebarGroup, OlSidebarItem, OlSidebarSubmenu, OlSkeleton, OlSpeedDial, OlSpeedDialItem, OlSpinner, OlStepItem, OlStepper, OlTabItem, OlTable, OlTableLoading, OlTableRow, OlTabs, OlTagInput, OlTextarea, OlTimeline, OlTimelineItem, OlTimepicker, OlToast, OlToastContainer, OlToastService, OlToggle, OlToggleGroup, OlToggleInput, OlTooltip, OlTooltipDirective, OlTree, OlTreeNodeItem, OlYearView, WEEKDAYS_SHORT_EN, WEEKDAYS_SHORT_VI, boDau, cloneDate, endOfDay, formatDate, generateMonthMatrix, generateMonthsMatrix, generateYearsMatrix, isBetween, isDateDisabled, isSameDay, isSameMonth, isSameYear, isToday, parseDate, startOfDay };
5234
+ export type { AccordionVariant, AlertColor, AlertDialogTone, AlertLayout, AlertRounded, AlertType, AlertVariant, AppDevice, AppTheme, AutocompleteOption, AutocompleteSize, AutocompleteState, AvatarShape, AvatarSize, AvatarStatus, BadgeShape, BadgeSize, BadgeTheme, BadgeType, BannerPosition, BannerVariant, BoxplotDataPoint, BreadcrumbItemStatus, BreadcrumbSeparator, BreadcrumbVariant, ButtonColor, ButtonGroupColor, ButtonGroupOrientation, ButtonGroupSize, ButtonSize, CardPadding, CardRadius, CardShadow, CardVariant, ChartDataPoint, ChartSeries, ChartType, ChatBubbleStatus, ChatSender, CheckboxSize, ChoiceControl, ChoiceControlPosition, ChoiceGroupLayout, ChoiceLayout, ChoiceShape, CommandGroup, CommandItem, DateCustomClass, DateRange, DatepickerPreset, DatepickerSelectionMode, DatepickerSize, DatepickerViewMode, DayCell, DeviceType, DrawerFooterLayout, DrawerPlacement, DrawerSize, DropdownAutoClose, DropdownPlacement, DropdownTrigger, EmptySize, FileInputSize, FloatingLabelSize, FloatingLabelState, FloatingLabelVariant, HeatmapData, HelperFeature, HelperRule, HelperSize, HelperState, IconColor, IconShapeColor, IconShapeSize, IconShapeType, IconSize, IconVariant, InputCounterSize, InputSize, InputState, JumbotronVariant, ListGroupType, ModalFooterLayout, ModalPlacement, ModalSize, MonthCell, OlAvatarStatusPosition, OlBottomNavPosition, OlButtonType, OlCheckboxRadioFormAlign, OlCheckboxRadioGroupRole, OlDateRangePickerVariant, OlGalleryGap, OlGalleryRatio, OlHelperType, OlModalBackdrop, OlModalRole, OlSkeletonAnimation, OlSkeletonAspectRatio, OlSkeletonRounded, OlSkeletonType, OlSpeedDialTooltipPosition, OlTimepickerAddonType, OlToggleInputAlign, OlTreeNode, OlTreeNodeSelectionMode, OlTreeNodeToggleIcon, OlTreeSelectionMode, OlTreeToggleIcon, OtpInputSize, PaginationType, PaginationVariant, PopoverPlacement, PopoverTrigger, PositionedCoordinates, PositioningOptions, ProgressColor, ProgressLabelPosition, ProgressSize, RadarIndicator, RadioSize, RatingType, ScatterDataPoint, ScatterSeries, ScrollAreaAxis, ScrollAreaScrollbar, SelectOption, SelectSize, SelectState, SeparatorOrientation, SpeedDialAction, SpeedDialColor, SpeedDialDirection, SpeedDialTrigger, SpeedDialType, StepState, StepperBreakpoint, StepperOrientation, StepperVariant, TabsSize, TabsVariant, TagInputSize, TextareaState, TimeObject, TimepickerFormat, TimepickerSize, TimepickerState, Toast, ToastConfig, ToastPosition, ToggleColor, TogglePosition, ToggleSize, YearCell };
4138
5235
  //# sourceMappingURL=edmicro-olkit.d.ts.map