@edmicro/olkit 1.1.1 → 1.1.2

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.
@@ -151,7 +151,13 @@ interface CommandGroup {
151
151
  * Phải xử lý `đ`/`Đ` riêng: chúng KHÔNG phải `d` cộng dấu phụ nên `NFD` không
152
152
  * tách ra được, `đăng nhập` sẽ không khớp `dang nhap` nếu bỏ bước này.
153
153
  */
154
- declare function boDau(chu: string): string;
154
+ declare function stripDiacritics(text: string): string;
155
+ /**
156
+ * @deprecated Dùng `stripDiacritics`. Giữ lại vì hàm này ĐÃ publish trong
157
+ * `@edmicro/olkit@1.1.1` (kiểm bằng `npm pack` rồi đọc `types/*.d.ts` — có
158
+ * trong danh sách export), bỏ đi là breaking change cho consumer.
159
+ */
160
+ declare const boDau: typeof stripDiacritics;
155
161
  /**
156
162
  * Bảng lệnh ⌘K — gõ để tìm rồi nhảy thẳng tới nơi cần.
157
163
  *
@@ -258,6 +264,38 @@ declare class OlButton {
258
264
  * Rỗng thì không phát ra thuộc tính, nút có chữ giữ nguyên tên từ nội dung.
259
265
  */
260
266
  readonly ariaLabel: _angular_core.InputSignal<string>;
267
+ /**
268
+ * Trạng thái mở/đóng của vùng mà nút này điều khiển.
269
+ *
270
+ * Cần vì đặt `[attr.aria-expanded]` thẳng lên `<ol-button>` **KHÔNG có tác
271
+ * dụng**: thuộc tính dừng ở thẻ bọc, không tới `<button>` bên trong — mà
272
+ * trình đọc màn hình đọc `<button>`. Đo `/collapse`: hai nút có
273
+ * `aria-expanded` trên host, `<button>` bên trong không có gì.
274
+ *
275
+ * Nhìn mã thì tưởng đã làm đúng trợ năng, nên đây là loại lỗi im lặng.
276
+ */
277
+ readonly ariaExpanded: _angular_core.InputSignal<boolean | null>;
278
+ /** `id` của vùng nội dung nút này điều khiển. Cùng lý do với `ariaExpanded`. */
279
+ readonly ariaControls: _angular_core.InputSignal<string>;
280
+ /** Trạng thái bật/tắt cho nút kiểu công tắc. Cùng lý do với `ariaExpanded`. */
281
+ readonly ariaPressed: _angular_core.InputSignal<boolean | null>;
282
+ /**
283
+ * Nội dung tooltip cho nút CHỈ-ICON, lấy từ `ariaLabel`.
284
+ *
285
+ * Đo 10 trang docs (BUG-018): **460 nút chỉ-icon, 0 nút câm** với trình đọc
286
+ * màn hình, nhưng **343 nút** có tên đọc được mà rê chuột không hiện gì.
287
+ * Chuẩn WAI-ARIA: nút chỉ-icon phải có nhãn NHÌN THẤY khi hover.
288
+ *
289
+ * Dùng `ariaLabel` sẵn có chứ không thêm input mới — cùng một thông tin.
290
+ *
291
+ * Chỉ bật cho `iconOnly`: nút có chữ đã tự hiện nghĩa, thêm tooltip lặp lại
292
+ * đúng dòng chữ đang nhìn là nhiễu.
293
+ *
294
+ * Rủi ro BUG-018 nêu ("chồng lên tooltip người dùng tự gắn") đã ĐO: hai
295
+ * directive lồng nhau, hover phần tử trong chỉ hiện MỘT tooltip vì
296
+ * `mouseenter` không nổi bọt. Xem `tooltip-nested.spec.ts`.
297
+ */
298
+ readonly tooltipText: _angular_core.Signal<string>;
261
299
  readonly btnClick: _angular_core.OutputEmitterRef<MouseEvent>;
262
300
  /**
263
301
  * Kích thước icon (px) theo bảng Figma ol-button.
@@ -268,7 +306,7 @@ declare class OlButton {
268
306
  readonly buttonClasses: _angular_core.Signal<string>;
269
307
  handleClick(event: MouseEvent): void;
270
308
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlButton, never>;
271
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlButton, "ol-button", never, { "color": { "alias": "color"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "pill": { "alias": "pill"; "required": false; "isSignal": true; }; "outline": { "alias": "outline"; "required": false; "isSignal": true; }; "iconOnly": { "alias": "iconOnly"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "btnClick": "btnClick"; }, never, ["[leftIcon]", "[logo]", "*", "[rightIcon]"], true, never>;
309
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlButton, "ol-button", never, { "color": { "alias": "color"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "pill": { "alias": "pill"; "required": false; "isSignal": true; }; "outline": { "alias": "outline"; "required": false; "isSignal": true; }; "iconOnly": { "alias": "iconOnly"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "ariaExpanded": { "alias": "ariaExpanded"; "required": false; "isSignal": true; }; "ariaControls": { "alias": "ariaControls"; "required": false; "isSignal": true; }; "ariaPressed": { "alias": "ariaPressed"; "required": false; "isSignal": true; }; }, { "btnClick": "btnClick"; }, never, ["[leftIcon]", "[logo]", "*", "[rightIcon]"], true, never>;
272
310
  }
273
311
 
274
312
  type IconShapeSize = 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl';
@@ -661,7 +699,7 @@ declare class OlCard {
661
699
  * `flat` và `outline` đều được bảng API mô tả là "không bóng" — gộp một chỗ
662
700
  * để năm binding bóng không phải lặp lại điều kiện.
663
701
  */
664
- readonly khongBong: _angular_core.Signal<boolean>;
702
+ readonly noShadow: _angular_core.Signal<boolean>;
665
703
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlCard, never>;
666
704
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlCard, "ol-card", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "padding": { "alias": "padding"; "required": false; "isSignal": true; }; "shadow": { "alias": "shadow"; "required": false; "isSignal": true; }; "rounded": { "alias": "rounded"; "required": false; "isSignal": true; }; "horizontal": { "alias": "horizontal"; "required": false; "isSignal": true; }; "imgSrc": { "alias": "imgSrc"; "required": false; "isSignal": true; }; "imgAlt": { "alias": "imgAlt"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "selected": { "alias": "selected"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
667
705
  }
@@ -733,22 +771,51 @@ declare class OlClipboard {
733
771
  readonly copied: _angular_core.WritableSignal<boolean>;
734
772
  /** Bao lâu thì nút quay lại biểu tượng sao chép, tính bằng mili giây. */
735
773
  readonly resetAfter: _angular_core.InputSignal<number>;
774
+ /**
775
+ * Tên đọc được cho nút. Nhánh "chưa sao chép" chỉ có SVG mang
776
+ * `aria-hidden="true"` nên nút HOÀN TOÀN CÂM với trình đọc màn hình — và đó
777
+ * là trạng thái người dùng thấy đầu tiên (BUG-013 mục 1b).
778
+ */
779
+ readonly ariaLabel: _angular_core.InputSignal<string>;
780
+ /** Chuỗi báo đã sao chép. Bản cũ khoá cứng tiếng Anh `Copied!`. */
781
+ readonly copiedText: _angular_core.InputSignal<string>;
736
782
  copy(): void;
737
783
  private markCopied;
738
784
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlClipboard, never>;
739
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlClipboard, "ol-clipboard", never, { "targetValue": { "alias": "targetValue"; "required": false; "isSignal": true; }; "resetAfter": { "alias": "resetAfter"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
785
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlClipboard, "ol-clipboard", never, { "targetValue": { "alias": "targetValue"; "required": false; "isSignal": true; }; "resetAfter": { "alias": "resetAfter"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "copiedText": { "alias": "copiedText"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
740
786
  }
741
787
 
742
788
  declare class OlCollapse {
743
789
  readonly collapsed: _angular_core.ModelSignal<boolean>;
790
+ /**
791
+ * `id` của vùng nội dung, để nút điều khiển bên ngoài trỏ tới bằng
792
+ * `aria-controls`.
793
+ *
794
+ * `ol-collapse` CHỈ bọc nội dung — nút bấm do người dùng tự đặt bên ngoài
795
+ * (xem demo `/collapse`), nên lib không gắn `aria-expanded` thay họ được.
796
+ * Cái lib làm được là cho vùng một `id` ổn định:
797
+ *
798
+ * ```html
799
+ * <ol-button [attr.aria-expanded]="!gap()" [attr.aria-controls]="noiDung.contentId()">
800
+ * {{ gap() ? 'Mở ra' : 'Thu gọn' }}
801
+ * </ol-button>
802
+ * <ol-collapse #noiDung [(collapsed)]="gap">…</ol-collapse>
803
+ * ```
804
+ */
805
+ readonly contentId: _angular_core.InputSignal<string>;
744
806
  toggle(): void;
745
807
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlCollapse, never>;
746
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlCollapse, "ol-collapse", never, { "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; }, { "collapsed": "collapsedChange"; }, never, ["*"], true, never>;
808
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlCollapse, "ol-collapse", never, { "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; "contentId": { "alias": "contentId"; "required": false; "isSignal": true; }; }, { "collapsed": "collapsedChange"; }, never, ["*"], true, never>;
747
809
  }
748
810
 
749
811
  type DatepickerViewMode = 'day' | 'month' | 'year';
750
812
  type DatepickerSelectionMode = 'single' | 'range';
751
- type DatepickerSize = 'sm' | 'base' | 'lg';
813
+ /**
814
+ * Thang cỡ khớp `ol-input` (`sm | base | lg | xl`) để đặt cạnh nhau không so
815
+ * le. Đo được trước khi thêm `xl`: `ol-input size="xl"` cao 52px mà datepicker
816
+ * không có cỡ tương ứng.
817
+ */
818
+ type DatepickerSize = 'sm' | 'base' | 'lg' | 'xl';
752
819
  type DateRange = [Date | null, Date | null];
753
820
  interface DateCustomClass {
754
821
  date: Date;
@@ -864,14 +931,18 @@ declare class OlDatepicker implements ControlValueAccessor {
864
931
  /**
865
932
  * Đẩy khung lịch ra `<body>` thay vì để trong cây DOM của component.
866
933
  *
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`.
934
+ * **Mặc định `'body'`** (từ bản này). Trước đây mặc định `null` — panel nằm
935
+ * trong cây DOM của component, nên hễ đặt vào modal / drawer / bảng có
936
+ * `overflow: hidden` là bị cắt, và người dùng phải tự biết mà bật. Đo
937
+ * /modal: khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING
938
+ * CONTEXT — z-index của lớp nổi không thoát ra được — và `overflow: hidden`
939
+ * CẮT luôn phần tràn. Hai lỗi chồng nhau, z-index một mình không chữa nổi
940
+ * cái thứ hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
872
941
  *
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.
942
+ * Đặt `null` để quay về hành vi cũ: cần khi CSS của nơi dùng nhắm vào panel
943
+ * qua cây DOM (`.form-cua-toi ol-select .panel`) — selector kiểu đó chết khi
944
+ * panel treo ra ngoài. Cùng tên và cùng ý nghĩa với `appendTo` của ng-select
945
+ * và PrimeNG.
875
946
  */
876
947
  readonly appendTo: _angular_core.InputSignal<"body" | null>;
877
948
  readonly showFooter: _angular_core.InputSignal<boolean>;
@@ -884,17 +955,39 @@ declare class OlDatepicker implements ControlValueAccessor {
884
955
  readonly formattedChange: _angular_core.OutputEmitterRef<string>;
885
956
  private readonly panel;
886
957
  private readonly overlayContainer;
887
- private readonly positioning;
888
958
  /** Hàm trả panel về chỗ cũ + hàm ngắt theo dõi vị trí. */
889
- private goPanel;
890
- private ngatTheoDoi;
959
+ private detachPanel;
960
+ /** Hàm gỡ theo dõi trần chiều cao popup. */
961
+ private stopTrackingHeight;
891
962
  readonly isOpen: _angular_core.WritableSignal<boolean>;
892
963
  readonly inputValue: _angular_core.WritableSignal<string>;
893
964
  private onChange;
894
965
  private onTouched;
895
966
  private emitAllChanges;
896
967
  constructor();
897
- readonly inputSizeClasses: _angular_core.Signal<"py-2 px-3 ps-9 text-xs rounded-lg" | "py-3.5 px-4 ps-11 text-base rounded-md" | "py-2.5 px-3.5 ps-10 text-sm rounded-md">;
968
+ /**
969
+ * Chiều cao khai bằng `h-*` CỐ ĐỊNH, không suy từ `py-*` + cỡ chữ.
970
+ *
971
+ * Bản cũ dùng `py-2 / py-2.5 / py-3.5` nên chiều cao phụ thuộc `line-height`
972
+ * của cỡ chữ và không bao giờ khớp thang `h-*` của họ form. Đo trên DOM thật
973
+ * (`/datepicker` và `/forms`, 8 ô nhập hiển thị):
974
+ *
975
+ * | cỡ | datepicker cũ | ol-input | lệch |
976
+ * |------|---------------|----------|------|
977
+ * | sm | 34px | 36px | −2 |
978
+ * | base | 42px | 40px | +2 |
979
+ * | lg | 54px | 48px | +6 |
980
+ *
981
+ * Đặt cạnh nhau trong một hàng là thấy so le ngay, nặng nhất ở `lg`.
982
+ *
983
+ * Thang `9/10/12` là chuẩn CHUNG của họ form, không phải chép riêng của
984
+ * `ol-input`: `ol-timepicker` dùng `h-9/h-10/h-12`, `ol-select` dùng
985
+ * `min-h-9/min-h-10/min-h-12`. Datepicker là component DUY NHẤT lạc thang.
986
+ *
987
+ * Bỏ `py-*`: chiều cao đã cố định thì đệm dọc chỉ ép chữ, giữ `px-*` cho đệm
988
+ * ngang và `ps-*` cho chỗ icon lịch.
989
+ */
990
+ readonly inputSizeClasses: _angular_core.Signal<"h-9 px-3 ps-9 text-xs rounded-md" | "h-12 px-4 ps-11 text-base rounded-md" | "h-13 px-4 ps-11 text-base rounded-md" | "h-10 px-3.5 ps-10 text-sm rounded-md">;
898
991
  /**
899
992
  * Cỡ icon lịch theo `size` của ô nhập.
900
993
  *
@@ -956,14 +1049,18 @@ declare class OlDateRangePicker implements ControlValueAccessor {
956
1049
  /**
957
1050
  * Đẩy khung lịch ra `<body>` thay vì để trong cây DOM của component.
958
1051
  *
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`.
1052
+ * **Mặc định `'body'`** (từ bản này). Trước đây mặc định `null` — panel nằm
1053
+ * trong cây DOM của component, nên hễ đặt vào modal / drawer / bảng có
1054
+ * `overflow: hidden` là bị cắt, và người dùng phải tự biết mà bật. Đo
1055
+ * /modal: khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING
1056
+ * CONTEXT — z-index của lớp nổi không thoát ra được — và `overflow: hidden`
1057
+ * CẮT luôn phần tràn. Hai lỗi chồng nhau, z-index một mình không chữa nổi
1058
+ * cái thứ hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
964
1059
  *
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.
1060
+ * Đặt `null` để quay về hành vi cũ: cần khi CSS của nơi dùng nhắm vào panel
1061
+ * qua cây DOM (`.form-cua-toi ol-select .panel`) — selector kiểu đó chết khi
1062
+ * panel treo ra ngoài. Cùng tên và cùng ý nghĩa với `appendTo` của ng-select
1063
+ * và PrimeNG.
967
1064
  */
968
1065
  readonly appendTo: _angular_core.InputSignal<"body" | null>;
969
1066
  readonly showFooter: _angular_core.InputSignal<boolean>;
@@ -974,10 +1071,10 @@ declare class OlDateRangePicker implements ControlValueAccessor {
974
1071
  readonly rangeChange: _angular_core.OutputEmitterRef<DateRange | null>;
975
1072
  private readonly panel;
976
1073
  private readonly overlayContainer;
977
- private readonly positioning;
978
1074
  /** Hàm trả panel về chỗ cũ + hàm ngắt theo dõi vị trí. */
979
- private goPanel;
980
- private ngatTheoDoi;
1075
+ private detachPanel;
1076
+ /** Hàm gỡ theo dõi trần chiều cao popup. */
1077
+ private stopTrackingHeight;
981
1078
  readonly isOpen: _angular_core.WritableSignal<boolean>;
982
1079
  readonly startInputValue: _angular_core.WritableSignal<string>;
983
1080
  readonly endInputValue: _angular_core.WritableSignal<string>;
@@ -990,7 +1087,30 @@ declare class OlDateRangePicker implements ControlValueAccessor {
990
1087
  * trong khi ô nhập co giãn theo `inputSizeClasses()`.
991
1088
  */
992
1089
  readonly iconSize: _angular_core.Signal<"xs" | "sm" | "base">;
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">;
1090
+ /**
1091
+ * Chiều cao `h-*` cố định — cùng luật với `ol-datepicker`, xem chú thích đầy
1092
+ * đủ ở đó. Tài liệu BUG-019 chỉ nêu `ol-datepicker`, nhưng component này
1093
+ * dùng y hệt `py-2 / py-2.5 / py-3.5` nên lệch cùng một kiểu; sửa một nửa là
1094
+ * để lại đúng lỗi đó ở chỗ khác.
1095
+ */
1096
+ readonly inputSizeClasses: _angular_core.Signal<"h-9 px-3 text-xs rounded-md" | "h-12 px-4 text-base rounded-md" | "h-13 px-4 text-base rounded-md" | "h-10 px-3.5 text-sm rounded-md">;
1097
+ /**
1098
+ * Cỡ cho biến thể `joined` — khung bọc mang viền, hai input bên trong trong
1099
+ * suốt. Phải tách khỏi `inputSizeClasses()`: ở đây chiều cao thuộc về KHUNG,
1100
+ * không thuộc về input.
1101
+ *
1102
+ * Trước đây nhánh `joined` khoá cứng `text-sm py-2` nên `[size]` không có
1103
+ * tác dụng gì. Đo /datepicker: `dual-inputs` ra 40px theo `h-10`, còn
1104
+ * `joined` luôn **36px** ở mọi cỡ — suy từ `py-2` + cỡ chữ, không theo thang
1105
+ * `h-*` của họ form. Đúng lỗi BUG-019 đã sửa cho `ol-datepicker`, sót lại ở
1106
+ * nhánh này.
1107
+ *
1108
+ * Khung có `p-1` = 4px mỗi cạnh, nên input con lùi 8px để TỔNG chiều cao
1109
+ * khớp thang: h-9 → h-7, h-10 → h-8, h-12 → h-10, h-13 → h-11.
1110
+ */
1111
+ readonly joinedWrapperClasses: _angular_core.Signal<"h-9" | "h-12" | "h-10" | "h-13">;
1112
+ /** Input bên trong khung `joined` — trừ đi `p-1` của khung. */
1113
+ readonly joinedInputClasses: _angular_core.Signal<"h-7 text-xs" | "h-10 text-base" | "h-11 text-base" | "h-8 text-sm">;
994
1114
  writeValue(val: DateRange | null): void;
995
1115
  registerOnChange(fn: (val: DateRange | null) => void): void;
996
1116
  registerOnTouched(fn: () => void): void;
@@ -1040,6 +1160,18 @@ declare class OlDatepickerHeader {
1040
1160
  declare class OlDayView {
1041
1161
  readonly state: OlDatepickerStateService;
1042
1162
  readonly customMatrix: _angular_core.InputSignal<DayCell[][] | null>;
1163
+ /**
1164
+ * Ghim chiều cao vùng lưới theo 6 hàng.
1165
+ *
1166
+ * Dùng khi popup hiện HAI tháng cạnh nhau: số hàng mỗi tháng khác nhau (4, 5
1167
+ * hoặc 6) nên hai lịch cao lệch, lịch bên phải thò xuống dưới. Đo cặp
1168
+ * 7+8/2025: tháng 7 cao 224px (5 hàng), tháng 8 cao 264px (6 hàng) — **lệch
1169
+ * 40px**.
1170
+ *
1171
+ * Ghim chiều cao chứ KHÔNG đệm thêm hàng rỗng: hàng rỗng lại tái diễn đúng
1172
+ * lỗi vừa sửa (dòng số mờ vô nghĩa dưới đáy lịch).
1173
+ */
1174
+ readonly fixedRows: _angular_core.InputSignal<boolean>;
1043
1175
  readonly daySelect: _angular_core.OutputEmitterRef<Date>;
1044
1176
  /** Nhãn thứ tiếng Việt theo Figma #1149:51169: CN · T2…T7. */
1045
1177
  readonly weekdays: string[];
@@ -1048,7 +1180,7 @@ declare class OlDayView {
1048
1180
  onMouseLeave(): void;
1049
1181
  getCellClasses(cell: DayCell): string[];
1050
1182
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlDayView, never>;
1051
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDayView, "ol-day-view", never, { "customMatrix": { "alias": "customMatrix"; "required": false; "isSignal": true; }; }, { "daySelect": "daySelect"; }, never, never, true, never>;
1183
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDayView, "ol-day-view", never, { "customMatrix": { "alias": "customMatrix"; "required": false; "isSignal": true; }; "fixedRows": { "alias": "fixedRows"; "required": false; "isSignal": true; }; }, { "daySelect": "daySelect"; }, never, never, true, never>;
1052
1184
  }
1053
1185
 
1054
1186
  declare class OlMonthView {
@@ -1089,6 +1221,67 @@ declare class OlDatepickerPopup {
1089
1221
  readonly showFooter: _angular_core.InputSignal<boolean>;
1090
1222
  readonly dualMonth: _angular_core.InputSignal<boolean>;
1091
1223
  readonly presets: _angular_core.InputSignal<DatepickerPreset[]>;
1224
+ /**
1225
+ * Lịch NHÚNG nằm trong luồng trang, không phải lớp nổi.
1226
+ *
1227
+ * Trần chiều cao và `overflow-y: auto` chỉ dành cho popup nổi — áp cho lịch
1228
+ * nhúng là sai: nó ăn giá trị dự phòng `85svh` dù không ai đặt biến. Đo ở
1229
+ * 414×500: lịch nhúng **bị cắt 56px**, footer chứa nút "Xác nhận" nằm NGOÀI
1230
+ * khung, và nó mọc thanh cuộn riêng trong khi người dùng đáng lẽ chỉ cần cuộn
1231
+ * trang như mọi nội dung khác.
1232
+ */
1233
+ readonly inline: _angular_core.InputSignal<boolean>;
1234
+ /**
1235
+ * Bề rộng màn tối thiểu để hiện hai lịch: hai lịch `w-63` (252px) + khe 24px
1236
+ * + đệm 40px ≈ 570, làm tròn lên 600 cho chắc.
1237
+ */
1238
+ private static readonly MIN_WIDTH_TWO_MONTHS;
1239
+ /**
1240
+ * Bề rộng MÀN HÌNH.
1241
+ *
1242
+ * Đã thử đo popup và đo `#panel` bằng ResizeObserver — cả hai đều SAI vì
1243
+ * chúng `absolute`/co theo nội dung, tạo vòng lặp phản hồi: hai lịch xuống
1244
+ * dòng làm khung hẹp lại, khung hẹp lại không kéo được về một lịch. Đo thật:
1245
+ * ở 1440px, đo popup ra 482 (vẫn 2 tháng xếp dọc), đo panel ra 440 (tụt về 1
1246
+ * tháng dù màn rất rộng).
1247
+ *
1248
+ * Màn hình là thước ỔN ĐỊNH duy nhất ở đây — nó không phụ thuộc nội dung.
1249
+ * Phần "chỗ thật còn lại bao nhiêu" vẫn do `flex-wrap` trong template lo, như
1250
+ * chú thích ở đó đã nêu; hai cơ chế bổ sung nhau chứ không chống nhau:
1251
+ * ngưỡng này cắt ca mobile (nơi hai lịch xếp dọc là không dùng được), còn
1252
+ * `flex-wrap` lo ca khung cha hẹp trên desktop.
1253
+ */
1254
+ private readonly screenWidth;
1255
+ /**
1256
+ * `dualMonth` là ý định của người dùng, `showTwoMonths` là thứ VẼ ĐƯỢC.
1257
+ *
1258
+ * Đo ở 375×812: hai lịch + footer cao 690px trong khi chỗ trống dưới ô nhập
1259
+ * chỉ ~320px — **49/70 ô ngày và toàn bộ nút "Xác nhận" nằm ngoài màn**,
1260
+ * người dùng không chốt nổi dải ngày. Hẹp thì hiện một tháng, điều hướng
1261
+ * bằng mũi tên vẫn đủ.
1262
+ */
1263
+ /**
1264
+ * Trần chiều cao + cuộn CHỈ cho popup nổi.
1265
+ *
1266
+ * Dùng `[class]` trả chuỗi chứ KHÔNG `[class.max-h-[var(...)]]`: tên class
1267
+ * chứa dấu chấm và ngoặc vuông nên Angular cắt hỏng tên khi dùng class
1268
+ * binding từng cái (`[class.h-1.5]` ra `h-1`).
1269
+ */
1270
+ readonly wrapperClasses: _angular_core.Signal<"" | "max-h-[var(--ol-dp-max-height,85svh)] overflow-y-auto overscroll-contain">;
1271
+ /**
1272
+ * Trần cho KHỐI LỊCH, để footer ghim lại còn vùng ngày cuộn.
1273
+ *
1274
+ * `wrapperClasses` đặt trần cho cả popup nên footer cuộn theo — đo
1275
+ * /datepicker ở 1278px: footer cao 49px, đáy ở 383 trong khung 350, tràn
1276
+ * 33px, nút "Xác nhận" phải cuộn mới thấy. Trần đặt thêm ở đây cho khối chứa
1277
+ * `[lịch + footer]`, còn `overflow` nằm ở riêng vùng lịch bên trong.
1278
+ *
1279
+ * Trừ đi `p-3.5`/`sm:p-5` của wrapper: dùng `calc` thay số cứng vì đệm đổi
1280
+ * theo breakpoint.
1281
+ */
1282
+ readonly calendarBlockClasses: _angular_core.Signal<"" | "max-h-[calc(var(--ol-dp-max-height,85svh)-2rem)] min-h-0">;
1283
+ readonly showTwoMonths: _angular_core.Signal<boolean>;
1284
+ constructor();
1092
1285
  readonly valueChange: _angular_core.OutputEmitterRef<Date | [Date | null, Date | null]>;
1093
1286
  readonly clearClick: _angular_core.OutputEmitterRef<void>;
1094
1287
  readonly applyClick: _angular_core.OutputEmitterRef<void>;
@@ -1100,7 +1293,7 @@ declare class OlDatepickerPopup {
1100
1293
  onFooterCancel(): void;
1101
1294
  applyPreset(preset: DatepickerPreset): void;
1102
1295
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlDatepickerPopup, never>;
1103
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDatepickerPopup, "ol-datepicker-popup", never, { "showFooter": { "alias": "showFooter"; "required": false; "isSignal": true; }; "dualMonth": { "alias": "dualMonth"; "required": false; "isSignal": true; }; "presets": { "alias": "presets"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; "clearClick": "clearClick"; "applyClick": "applyClick"; "cancelClick": "cancelClick"; }, never, never, true, never>;
1296
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDatepickerPopup, "ol-datepicker-popup", never, { "showFooter": { "alias": "showFooter"; "required": false; "isSignal": true; }; "dualMonth": { "alias": "dualMonth"; "required": false; "isSignal": true; }; "presets": { "alias": "presets"; "required": false; "isSignal": true; }; "inline": { "alias": "inline"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; "clearClick": "clearClick"; "applyClick": "applyClick"; "cancelClick": "cancelClick"; }, never, never, true, never>;
1104
1297
  }
1105
1298
 
1106
1299
  /**
@@ -1125,7 +1318,16 @@ declare function isDateDisabled(date: Date, minDate?: Date | null, maxDate?: Dat
1125
1318
  declare function formatDate(date: Date | null | undefined, format?: string): string;
1126
1319
  declare function parseDate(dateStr: string, format?: string): Date | null;
1127
1320
  /**
1128
- * Generates 42 days grid (6 weeks x 7 days) for the calendar view
1321
+ * Dựng lưới ngày cho khung lịch: 6 tuần, rồi BỎ tuần cuối nếu nó không chứa
1322
+ * ngày nào của tháng đang xem.
1323
+ *
1324
+ * Vì sao: bản cũ luôn trả cứng 6 hàng. Đo 84 tháng (2024–2030) thì **66 tháng
1325
+ * (78,6%)** có hàng thứ 6 gồm TOÀN ngày tháng sau — một dòng số mờ vô nghĩa
1326
+ * nằm dưới cùng, người dùng tưởng bấm được. Không phải ca hiếm mà là đa số.
1327
+ *
1328
+ * Không cắt cứng xuống 5 hàng: 18 tháng còn lại (ví dụ 8/2026, hàng cuối
1329
+ * `30 31 1 2 3 4 5`) thực sự cần hàng thứ 6 — cắt cứng là mất ngày 30, 31.
1330
+ * Điều kiện phải là "toàn bộ hàng nằm ngoài tháng", không phải "đủ 6 hàng".
1129
1331
  */
1130
1332
  declare function generateMonthMatrix(viewDate: Date, options?: {
1131
1333
  selectedDate?: Date | null;
@@ -1247,20 +1449,24 @@ declare class OlDropdown {
1247
1449
  private readonly overlayContainer;
1248
1450
  private cleanupAutoUpdate;
1249
1451
  /** Hàm trả menu về chỗ cũ khi đóng. */
1250
- private goMenu;
1452
+ private detachMenu;
1251
1453
  readonly label: _angular_core.InputSignal<string | null>;
1252
1454
  readonly isOpen: _angular_core.ModelSignal<boolean>;
1253
1455
  /**
1254
1456
  * Đẩy menu ra `<body>` thay vì để trong cây DOM của component.
1255
1457
  *
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`.
1458
+ * **Mặc định `'body'`** (từ bản này). Trước đây mặc định `null` — panel nằm
1459
+ * trong cây DOM của component, nên hễ đặt vào modal / drawer / bảng có
1460
+ * `overflow: hidden` là bị cắt, và người dùng phải tự biết mà bật. Đo
1461
+ * /modal: khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING
1462
+ * CONTEXT — z-index của lớp nổi không thoát ra được — và `overflow: hidden`
1463
+ * CẮT luôn phần tràn. Hai lỗi chồng nhau, z-index một mình không chữa nổi
1464
+ * cái thứ hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
1261
1465
  *
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.
1466
+ * Đặt `null` để quay về hành vi cũ: cần khi CSS của nơi dùng nhắm vào panel
1467
+ * qua cây DOM (`.form-cua-toi ol-select .panel`) — selector kiểu đó chết khi
1468
+ * panel treo ra ngoài. Cùng tên và cùng ý nghĩa với `appendTo` của ng-select
1469
+ * và PrimeNG.
1264
1470
  */
1265
1471
  readonly appendTo: _angular_core.InputSignal<"body" | null>;
1266
1472
  readonly placement: _angular_core.InputSignal<DropdownPlacement>;
@@ -1565,7 +1771,28 @@ declare class OlCheckboxRadioForm implements ControlValueAccessor {
1565
1771
  /** Tên nhóm cho ô tròn — cùng `name` thì chỉ chọn được một. */
1566
1772
  readonly name: _angular_core.InputSignal<string>;
1567
1773
  readonly value: _angular_core.InputSignal<string>;
1568
- readonly inputId: _angular_core.InputSignal<string>;
1774
+ /**
1775
+ * Id của ô nhập, để `<label for>` nối được.
1776
+ *
1777
+ * TỰ SINH khi nơi gọi không truyền. Mặc định RỖNG trước đây làm
1778
+ * `[attr.id]="inputId() || null"` ra `null`, nên `<ol-label [for]="">` trỏ
1779
+ * vào hư không: đo `/checkbox-radio` ra **29 control không tên** trong cây
1780
+ * trợ năng dù nhãn hiển thị đẹp trên màn hình (BUG-031).
1781
+ *
1782
+ * Cùng nếp với `ol-input`, `ol-checkbox`, `ol-select` — chúng đều
1783
+ * `computed(() => this.id() || this.uid)`.
1784
+ */
1785
+ private readonly uid;
1786
+ readonly inputIdInput: _angular_core.InputSignal<string>;
1787
+ readonly inputId: _angular_core.Signal<string>;
1788
+ /**
1789
+ * Tên trợ năng khi thẻ KHÔNG có nhãn chữ.
1790
+ *
1791
+ * Ca `control="none"` kèm `icon`: thẻ chỉ là một ô vuông có icon, `label()`
1792
+ * rỗng nên `<ol-label>` không render. Đo `/checkbox-radio`: ô nhập không
1793
+ * tên trong cây trợ năng dù bấm được (BUG-031).
1794
+ */
1795
+ readonly ariaLabel: _angular_core.InputSignal<string>;
1569
1796
  readonly changed: _angular_core.OutputEmitterRef<boolean>;
1570
1797
  /** `none` vẫn phải bấm được, nên hàng luôn là nhãn bấm được trừ khi bị khoá. */
1571
1798
  readonly interactive: _angular_core.Signal<boolean>;
@@ -1588,14 +1815,14 @@ declare class OlCheckboxRadioForm implements ControlValueAccessor {
1588
1815
  private onChange;
1589
1816
  private onTouched;
1590
1817
  /** 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;
1818
+ private initialized;
1592
1819
  constructor();
1593
1820
  writeValue(val: boolean): void;
1594
1821
  registerOnChange(fn: (val: boolean) => void): void;
1595
1822
  registerOnTouched(fn: () => void): void;
1596
1823
  setDisabledState(isDisabled: boolean): void;
1597
1824
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlCheckboxRadioForm, never>;
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>;
1825
+ 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; }; "inputIdInput": { "alias": "inputId"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "checked": "checkedChange"; "disabled": "disabledChange"; "changed": "changed"; }, never, ["[lead]", "*"], true, never>;
1599
1826
  }
1600
1827
 
1601
1828
  /** Xếp dọc, xếp ngang, hay lưới nhiều cột. */
@@ -1669,6 +1896,16 @@ declare class OlInput implements ControlValueAccessor {
1669
1896
  readonly showAsterisk: _angular_core.InputSignal<boolean>;
1670
1897
  readonly labelIcon: _angular_core.InputSignal<boolean>;
1671
1898
  readonly labelTooltip: _angular_core.InputSignal<string>;
1899
+ /**
1900
+ * Tên trợ năng khi ô nhập KHÔNG có nhãn nhìn thấy được.
1901
+ *
1902
+ * `<label for>` chỉ render khi `label()` có chữ (`@if (label())` ở đầu
1903
+ * template), nên bố cục đặt chữ BÊN NGOÀI component — bảng lọc, thanh công
1904
+ * cụ, trang cài đặt — để lại một `<input>` hoàn toàn không tên. Có `label()`
1905
+ * thì không cần truyền cái này; hai thứ cùng lúc thì `aria-label` thắng và
1906
+ * người dùng bàn phím nghe một đằng, nhìn một nẻo.
1907
+ */
1908
+ readonly ariaLabel: _angular_core.InputSignal<string>;
1672
1909
  readonly prefixText: _angular_core.InputSignal<string>;
1673
1910
  readonly suffixText: _angular_core.InputSignal<string>;
1674
1911
  readonly addOnIcon: _angular_core.InputSignal<boolean>;
@@ -1706,9 +1943,9 @@ declare class OlInput implements ControlValueAccessor {
1706
1943
  /** Gọi từ template mỗi khi giá trị đổi, để form biết. */
1707
1944
  protected phatThayDoi(val: string): void;
1708
1945
  /** Gọi khi rời ô nhập — form dùng để đánh dấu `touched`. */
1709
- protected danhDauCham(): void;
1946
+ protected markTouched(): void;
1710
1947
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlInput, 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>;
1948
+ 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; }; "ariaLabel": { "alias": "ariaLabel"; "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>;
1712
1949
  }
1713
1950
 
1714
1951
  type TextareaState = 'default' | 'error' | 'success';
@@ -1722,6 +1959,12 @@ declare class OlTextarea implements ControlValueAccessor {
1722
1959
  readonly ariaInvalid: _angular_core.Signal<"true" | null>;
1723
1960
  readonly value: _angular_core.ModelSignal<string>;
1724
1961
  readonly label: _angular_core.InputSignal<string>;
1962
+ /**
1963
+ * Tên trợ năng khi control KHÔNG có nhãn nhìn thấy được. Cùng lý do với
1964
+ * `ol-input.ariaLabel`: `<label>` chỉ render khi `label()` có chữ, nên bố
1965
+ * cục đặt chữ BÊN NGOÀI component để lại một control hoàn toàn không tên.
1966
+ */
1967
+ readonly ariaLabel: _angular_core.InputSignal<string>;
1725
1968
  readonly placeholder: _angular_core.InputSignal<string>;
1726
1969
  readonly rows: _angular_core.InputSignal<number>;
1727
1970
  /**
@@ -1754,14 +1997,24 @@ declare class OlTextarea implements ControlValueAccessor {
1754
1997
  /** Gọi từ template mỗi khi giá trị đổi, để form biết. */
1755
1998
  protected phatThayDoi(val: string): void;
1756
1999
  /** Gọi khi rời ô nhập — form dùng để đánh dấu `touched`. */
1757
- protected danhDauCham(): void;
2000
+ protected markTouched(): void;
1758
2001
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlTextarea, 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>;
2002
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTextarea, "ol-textarea", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "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>;
1760
2003
  }
1761
2004
 
1762
2005
  /**
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.
2006
+ * Thang cỡ theo Figma: `sm | base | lg`.
2007
+ *
2008
+ * `'md'` là **BÍ DANH `@deprecated` của `'base'`** — quét toàn repo: **0 chỗ
2009
+ * dùng** (cả docs app lẫn lib). Giữ cho mã ngoài đang gọi vì `@edmicro/olkit`
2010
+ * đã publish công khai; bỏ input công khai là breaking change cho consumer mà
2011
+ * repo không đo được. Dùng `'base'` cho mã mới.
2012
+ *
2013
+ * Cùng cách xử lý với `variant`/`currentPage` của `ol-pagination`.
2014
+ *
2015
+ * LƯU Ý cho lượt sau: họ form nay là `sm | base | lg | xl` (`ol-input`,
2016
+ * `ol-timepicker`, `ol-datepicker`). `ol-select` **thiếu `xl`** — thêm được
2017
+ * nhưng là mở rộng API, cần chốt trước.
1765
2018
  */
1766
2019
  type SelectSize = 'sm' | 'base' | 'lg' | 'md';
1767
2020
  type SelectState = 'default' | 'error' | 'success';
@@ -1799,14 +2052,14 @@ interface SelectOption {
1799
2052
  [key: string]: any;
1800
2053
  }
1801
2054
  /** 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;
2055
+ interface FlatRow {
2056
+ kind: 'group' | 'option';
2057
+ label: string;
1805
2058
  item?: any;
1806
2059
  /** 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;
2060
+ index: number;
2061
+ /** Chỉ ở dòng `group`: đây có phải nhóm ĐẦU TIÊN không (để không kẻ đường ngăn thừa). */
2062
+ isFirstGroup?: boolean;
1810
2063
  }
1811
2064
  /**
1812
2065
  * `ol-select` — ô chọn có TÌM KIẾM, ĐA CHỌN, GOM NHÓM và CUỘN ẢO.
@@ -1835,6 +2088,11 @@ declare class OlSelect implements ControlValueAccessor {
1835
2088
  readonly value: _angular_core.ModelSignal<any>;
1836
2089
  readonly options: _angular_core.InputSignal<SelectOption[]>;
1837
2090
  readonly label: _angular_core.InputSignal<string>;
2091
+ /**
2092
+ * Tên trợ năng khi KHÔNG có `label()` nhìn thấy được. Có `label()` thì
2093
+ * `aria-labelledby` đã lo — không cần truyền cái này.
2094
+ */
2095
+ readonly ariaLabel: _angular_core.InputSignal<string>;
1838
2096
  readonly size: _angular_core.InputSignal<SelectSize>;
1839
2097
  readonly helperText: _angular_core.InputSignal<string>;
1840
2098
  readonly state: _angular_core.InputSignal<SelectState>;
@@ -1861,14 +2119,18 @@ declare class OlSelect implements ControlValueAccessor {
1861
2119
  /**
1862
2120
  * Đẩy danh sách ra `<body>` thay vì để trong cây DOM của component.
1863
2121
  *
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.
2122
+ * **Mặc định `'body'`** (từ bản này). Trước đây mặc định `null` — panel nằm
2123
+ * trong cây DOM của component, nên hễ đặt vào modal / drawer / bảng có
2124
+ * `overflow: hidden` là bị cắt, và người dùng phải tự biết mà bật. Đo
2125
+ * /modal: khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING
2126
+ * CONTEXT — z-index của lớp nổi không thoát ra được — và `overflow: hidden`
2127
+ * CẮT luôn phần tràn. Hai lỗi chồng nhau, z-index một mình không chữa nổi
2128
+ * cái thứ hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
1869
2129
  *
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.
2130
+ * Đặt `null` để quay về hành vi cũ: cần khi CSS của nơi dùng nhắm vào panel
2131
+ * qua cây DOM (`.form-cua-toi ol-select .panel`) — selector kiểu đó chết khi
2132
+ * panel treo ra ngoài. Cùng tên và cùng ý nghĩa với `appendTo` của ng-select
2133
+ * và PrimeNG.
1872
2134
  */
1873
2135
  readonly appendTo: _angular_core.InputSignal<"body" | null>;
1874
2136
  /** Tối đa bao nhiêu mục được chọn ở chế độ đa chọn. 0 = không giới hạn. */
@@ -1970,7 +2232,7 @@ declare class OlSelect implements ControlValueAccessor {
1970
2232
  */
1971
2233
  private readonly panel;
1972
2234
  /** Vùng CUỘN — là thẻ `role="listbox"`, không phải hộp bọc. */
1973
- private readonly danhSach;
2235
+ private readonly listboxRef;
1974
2236
  /** Ô tìm ở đầu panel, để chuyển focus vào khi mở. */
1975
2237
  private readonly oTimPanel;
1976
2238
  readonly isOpen: _angular_core.WritableSignal<boolean>;
@@ -1981,6 +2243,24 @@ declare class OlSelect implements ControlValueAccessor {
1981
2243
  private readonly uid;
1982
2244
  readonly inputId: _angular_core.Signal<string>;
1983
2245
  readonly helperId: _angular_core.Signal<string>;
2246
+ /**
2247
+ * Id của `<label>`, để combobox trỏ vào bằng `aria-labelledby`.
2248
+ *
2249
+ * `<label for>` chỉ đặt tên cho form control GỐC (`input`, `select`,
2250
+ * `textarea`, `button`) — KHÔNG cho `<div role="combobox">` dù id khớp
2251
+ * hoàn toàn. Đo `/select`: 32/32 combobox không có tên trong cây trợ năng
2252
+ * trong khi `labelFor` và `idCombobox` khớp y nhau (BUG-030).
2253
+ */
2254
+ readonly labelId: _angular_core.Signal<string>;
2255
+ /**
2256
+ * Id của nhãn cố định NẰM TRONG ô đóng (`triggerLabel`).
2257
+ *
2258
+ * Chữ đó đã có trong `textContent` của combobox, nhưng `role="combobox"`
2259
+ * khiến trình đọc màn hình coi nội dung là GIÁ TRỊ chứ không phải TÊN —
2260
+ * nghe ra "combobox, Khối: Khối 10" mà vẫn không biết đang chọn cái gì.
2261
+ * Trỏ vào chính `<span>` đó nên tên nghe được trùng khít chữ nhìn thấy.
2262
+ */
2263
+ readonly triggerLabelId: _angular_core.Signal<string>;
1984
2264
  readonly listboxId: _angular_core.Signal<string>;
1985
2265
  readonly describedBy: _angular_core.Signal<string | null>;
1986
2266
  readonly ariaInvalid: _angular_core.Signal<"true" | null>;
@@ -1996,11 +2276,10 @@ declare class OlSelect implements ControlValueAccessor {
1996
2276
  private readonly destroyRef;
1997
2277
  private readonly elementRef;
1998
2278
  private readonly overlayContainer;
1999
- private readonly positioning;
2000
2279
  /** 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;
2280
+ private detachPanel;
2281
+ private stopTracking;
2282
+ private wasOpen;
2004
2283
  private hengio;
2005
2284
  constructor();
2006
2285
  /** Ngắt theo dõi vị trí và trả panel về chỗ cũ. Gọi được nhiều lần. */
@@ -2010,7 +2289,7 @@ declare class OlSelect implements ControlValueAccessor {
2010
2289
  * trị mặc định, và `x < undefined` ra `NaN` — luôn `false`. Cùng lớp lỗi
2011
2290
  * BUG-009, nên mọi input số đều đi qua một hàm lọc.
2012
2291
  */
2013
- private soDuong;
2292
+ private positiveOr;
2014
2293
  private readonly safeMinTerm;
2015
2294
  private readonly safeDebounce;
2016
2295
  private readonly safeItemHeight;
@@ -2018,7 +2297,7 @@ declare class OlSelect implements ControlValueAccessor {
2018
2297
  readonly safeMaxSelected: _angular_core.Signal<number>;
2019
2298
  getItemLabel(item: any): string;
2020
2299
  getItemValue(item: any): any;
2021
- private bangNhau;
2300
+ private sameValue;
2022
2301
  /**
2023
2302
  * Giá trị đang chọn, LUÔN trả mảng — template chỉ có một đường đọc.
2024
2303
  *
@@ -2050,15 +2329,15 @@ declare class OlSelect implements ControlValueAccessor {
2050
2329
  */
2051
2330
  readonly selectedItem: _angular_core.Signal<SelectOption | null>;
2052
2331
  /** 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;
2332
+ optionOf(val: any): SelectOption | null;
2054
2333
  /** Mục này có phần đầu (icon hoặc chữ) để vẽ không. */
2055
- coPhanDau(item: SelectOption | null | undefined): boolean;
2334
+ hasPrefix(item: SelectOption | null | undefined): boolean;
2056
2335
  /**
2057
2336
  * 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
2337
  * `hasValue()`. Khi mọi mục đã chọn đều `locked`, nút hiện mà bấm không làm
2059
2338
  * gì: nút chết còn khó hiểu hơn là không có nút.
2060
2339
  */
2061
- readonly coTheXoa: _angular_core.Signal<boolean>;
2340
+ readonly hasClearable: _angular_core.Signal<boolean>;
2062
2341
  /**
2063
2342
  * Có hiện lối tạo mục mới không.
2064
2343
  *
@@ -2066,7 +2345,7 @@ declare class OlSelect implements ControlValueAccessor {
2066
2345
  * 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
2346
  * mới" cùng tên chỉ làm họ tạo ra bản trùng.
2068
2347
  */
2069
- readonly coTheTao: _angular_core.Signal<boolean>;
2348
+ readonly canCreate: _angular_core.Signal<boolean>;
2070
2349
  /** Chữ trên nút tạo mới, đã thay `{term}`. */
2071
2350
  readonly createLabel: _angular_core.Signal<string>;
2072
2351
  /**
@@ -2076,7 +2355,7 @@ declare class OlSelect implements ControlValueAccessor {
2076
2355
  * 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
2356
  * nghe `added` rồi tự quyết có đưa vào danh sách hay không.
2078
2357
  */
2079
- taoMuc(): void;
2358
+ createOption(): void;
2080
2359
  readonly waitingForTerm: _angular_core.Signal<boolean>;
2081
2360
  readonly filteredOptions: _angular_core.Signal<SelectOption[]>;
2082
2361
  /**
@@ -2086,17 +2365,26 @@ declare class OlSelect implements ControlValueAccessor {
2086
2365
  * theo chỉ số phẳng; lồng vào thì `$index` reset mỗi nhóm và điều hướng bàn
2087
2366
  * phím sai hết. Cùng lý do đã ghi ở `ol-autocomplete`.
2088
2367
  */
2089
- readonly rows: _angular_core.Signal<Dong[]>;
2368
+ readonly rows: _angular_core.Signal<FlatRow[]>;
2090
2369
  /** Chỉ các dòng CHỌN ĐƯỢC — bàn phím chạy trên danh sách này. */
2091
2370
  readonly selectableItems: _angular_core.Signal<SelectOption[]>;
2092
2371
  /**
2093
2372
  * 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.
2373
+ * panel. CHƯA CHỌN GÌ thì trả `-1`: không mục nào được đánh dấu.
2374
+ *
2375
+ * Bản trước trả `0` cho ca chưa chọn, nên mở dropdown ra là option đầu đã có
2376
+ * nền xám đặc trong khi `aria-selected="false"` — người dùng NHÌN thấy "đã
2377
+ * chọn Toán" còn trình đọc màn hình NGHE "chưa chọn gì". Nặng hơn: bấm Enter
2378
+ * ngay lúc đó là **chọn nhầm option đầu**. Đo /select: 5/5 ô chưa chọn đều
2379
+ * dính (BUG-021).
2380
+ *
2381
+ * Đây đúng là hậu quả mà chú thích của `open()` từng cảnh báo cho ca ĐÃ có giá
2382
+ * trị — bản sửa đó chữa nửa trên mà giữ nguyên nửa dưới.
2095
2383
  *
2096
2384
  * Đ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
2385
  * đã 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
2386
  */
2099
- private chiSoMucDangChon;
2387
+ private selectedOptionIndex;
2100
2388
  /**
2101
2389
  * Khoảng dòng thật sự được vẽ. Nguyên lý lấy từ `ng-select`:
2102
2390
  * `scrollTop / itemHeight` cho mục đầu trong tầm nhìn, cộng thêm số dòng
@@ -2106,14 +2394,23 @@ declare class OlSelect implements ControlValueAccessor {
2106
2394
  * trong template.
2107
2395
  */
2108
2396
  readonly viewport: _angular_core.Signal<{
2109
- rows: Dong[];
2110
- dauPad: number;
2111
- cuoiPad: number;
2397
+ rows: FlatRow[];
2398
+ padTop: number;
2399
+ padBottom: number;
2112
2400
  }>;
2113
2401
  onPanelScroll(e: Event): void;
2114
2402
  onDocumentClick(e: MouseEvent): void;
2115
2403
  toggle(): void;
2404
+ open(): void;
2405
+ close(): void;
2406
+ /**
2407
+ * @deprecated Dùng `open()`. Giữ lại vì `@edmicro/olkit` đã publish lên npm
2408
+ * công khai — bỏ phương thức công khai là breaking change cho consumer mà
2409
+ * repo này không đo được. Theo đúng nếp `@deprecated` ở `variant` của
2410
+ * `ol-pagination` và `onPageSizeChangeEvent`.
2411
+ */
2116
2412
  mo(): void;
2413
+ /** @deprecated Dùng `close()`. Cùng lý do với `mo()`. */
2117
2414
  dong(): void;
2118
2415
  onSearchInput(val: string): void;
2119
2416
  /**
@@ -2122,7 +2419,7 @@ declare class OlSelect implements ControlValueAccessor {
2122
2419
  */
2123
2420
  private phatSearch;
2124
2421
  /** Đơ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;
2422
+ private shouldClose;
2126
2423
  selectItem(item: any): void;
2127
2424
  /**
2128
2425
  * Mục này có bị khoá không gỡ không.
@@ -2134,16 +2431,16 @@ declare class OlSelect implements ControlValueAccessor {
2134
2431
  isLocked(val: any): boolean;
2135
2432
  removeValue(val: any, e: Event): void;
2136
2433
  clear(e: Event): void;
2137
- private datGiaTri;
2434
+ private setValue;
2138
2435
  onKeyDown(e: KeyboardEvent): void;
2139
- private diChuyen;
2436
+ private moveActive;
2140
2437
  /**
2141
2438
  * Kéo mục đang đánh dấu vào tầm nhìn.
2142
2439
  *
2143
2440
  * 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.
2441
+ * Tính thẳng `index * itemHeight` mới đúng.
2145
2442
  */
2146
- private cuonToiMuc;
2443
+ private scrollToOption;
2147
2444
  optionId(i: number): string;
2148
2445
  readonly activeOptionId: _angular_core.Signal<string | null>;
2149
2446
  writeValue(val: any): void;
@@ -2151,7 +2448,7 @@ declare class OlSelect implements ControlValueAccessor {
2151
2448
  registerOnTouched(fn: () => void): void;
2152
2449
  setDisabledState(isDisabled: boolean): void;
2153
2450
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlSelect, 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>;
2451
+ 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; }; "ariaLabel": { "alias": "ariaLabel"; "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>;
2155
2452
  }
2156
2453
 
2157
2454
  /**
@@ -2162,6 +2459,12 @@ type CheckboxSize = 'sm' | 'base' | 'lg' | 'md';
2162
2459
  declare class OlCheckbox implements ControlValueAccessor {
2163
2460
  readonly checked: _angular_core.ModelSignal<boolean>;
2164
2461
  readonly label: _angular_core.InputSignal<string>;
2462
+ /**
2463
+ * Tên trợ năng khi control KHÔNG có nhãn nhìn thấy được. Cùng lý do với
2464
+ * `ol-input.ariaLabel`: `<label>` chỉ render khi `label()` có chữ, nên bố
2465
+ * cục đặt chữ BÊN NGOÀI component để lại một control hoàn toàn không tên.
2466
+ */
2467
+ readonly ariaLabel: _angular_core.InputSignal<string>;
2165
2468
  readonly description: _angular_core.InputSignal<string>;
2166
2469
  /**
2167
2470
  * Khai bằng `model()` thay vì `input()` để Reactive Forms khoá được component:
@@ -2200,7 +2503,7 @@ declare class OlCheckbox implements ControlValueAccessor {
2200
2503
  registerOnTouched(fn: any): void;
2201
2504
  setDisabledState(isDisabled: boolean): void;
2202
2505
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlCheckbox, never>;
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>;
2506
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlCheckbox, "ol-checkbox", never, { "checked": { "alias": "checked"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "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>;
2204
2507
  }
2205
2508
 
2206
2509
  /**
@@ -2214,6 +2517,12 @@ declare class OlRadio implements ControlValueAccessor {
2214
2517
  readonly selectedValue: _angular_core.ModelSignal<any>;
2215
2518
  readonly modelValue: _angular_core.ModelSignal<any>;
2216
2519
  readonly label: _angular_core.InputSignal<string>;
2520
+ /**
2521
+ * Tên trợ năng khi control KHÔNG có nhãn nhìn thấy được. Cùng lý do với
2522
+ * `ol-input.ariaLabel`: `<label>` chỉ render khi `label()` có chữ, nên bố
2523
+ * cục đặt chữ BÊN NGOÀI component để lại một control hoàn toàn không tên.
2524
+ */
2525
+ readonly ariaLabel: _angular_core.InputSignal<string>;
2217
2526
  readonly description: _angular_core.InputSignal<string>;
2218
2527
  /**
2219
2528
  * Khai bằng `model()` thay vì `input()` để Reactive Forms khoá được component:
@@ -2238,7 +2547,7 @@ declare class OlRadio implements ControlValueAccessor {
2238
2547
  registerOnTouched(fn: any): void;
2239
2548
  setDisabledState(isDisabled: boolean): void;
2240
2549
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlRadio, never>;
2241
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlRadio, "ol-radio", never, { "name": { "alias": "name"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "selectedValue": { "alias": "selectedValue"; "required": false; "isSignal": true; }; "modelValue": { "alias": "modelValue"; "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; }; "card": { "alias": "card"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "checked": { "alias": "checked"; "required": false; "isSignal": true; }; }, { "selectedValue": "selectedValueChange"; "modelValue": "modelValueChange"; "disabled": "disabledChange"; }, never, ["*"], true, never>;
2550
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlRadio, "ol-radio", never, { "name": { "alias": "name"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "selectedValue": { "alias": "selectedValue"; "required": false; "isSignal": true; }; "modelValue": { "alias": "modelValue"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "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; }; "card": { "alias": "card"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "checked": { "alias": "checked"; "required": false; "isSignal": true; }; }, { "selectedValue": "selectedValueChange"; "modelValue": "modelValueChange"; "disabled": "disabledChange"; }, never, ["*"], true, never>;
2242
2551
  }
2243
2552
 
2244
2553
  /**
@@ -2250,6 +2559,12 @@ type ToggleColor = 'brand' | 'orange' | 'green' | 'red';
2250
2559
  declare class OlToggle implements ControlValueAccessor {
2251
2560
  readonly checked: _angular_core.ModelSignal<boolean>;
2252
2561
  readonly label: _angular_core.InputSignal<string>;
2562
+ /**
2563
+ * Tên trợ năng khi control KHÔNG có nhãn nhìn thấy được. Cùng lý do với
2564
+ * `ol-input.ariaLabel`: `<label>` chỉ render khi `label()` có chữ, nên bố
2565
+ * cục đặt chữ BÊN NGOÀI component để lại một control hoàn toàn không tên.
2566
+ */
2567
+ readonly ariaLabel: _angular_core.InputSignal<string>;
2253
2568
  readonly description: _angular_core.InputSignal<string>;
2254
2569
  /**
2255
2570
  * Khai bằng `model()` thay vì `input()` để Reactive Forms khoá được component:
@@ -2271,7 +2586,7 @@ declare class OlToggle implements ControlValueAccessor {
2271
2586
  registerOnTouched(fn: any): void;
2272
2587
  setDisabledState(isDisabled: boolean): void;
2273
2588
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlToggle, never>;
2274
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlToggle, "ol-toggle", 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; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "justifyBetween": { "alias": "justifyBetween"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, { "checked": "checkedChange"; "disabled": "disabledChange"; }, never, never, true, never>;
2589
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlToggle, "ol-toggle", never, { "checked": { "alias": "checked"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "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; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "justifyBetween": { "alias": "justifyBetween"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, { "checked": "checkedChange"; "disabled": "disabledChange"; }, never, never, true, never>;
2275
2590
  }
2276
2591
 
2277
2592
  /** Công tắc đứng bên nào của hàng. Figma `Type=Default` để bên trái. */
@@ -2322,7 +2637,7 @@ declare class OlToggleInput implements ControlValueAccessor {
2322
2637
  private onChange;
2323
2638
  private onTouched;
2324
2639
  /** 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;
2640
+ private initialized;
2326
2641
  constructor();
2327
2642
  writeValue(val: boolean): void;
2328
2643
  registerOnChange(fn: (val: boolean) => void): void;
@@ -2416,6 +2731,29 @@ declare class OlRange implements ControlValueAccessor {
2416
2731
  private readonly generatedId;
2417
2732
  protected readonly inputId: _angular_core.Signal<string>;
2418
2733
  readonly helperId: _angular_core.Signal<string>;
2734
+ /**
2735
+ * Id riêng cho HAI tay trượt ở chế độ `dualRange`.
2736
+ *
2737
+ * Nhánh `dualRange` trước đây không có `[id]`, không `aria-label`, không
2738
+ * `aria-describedby` — `<label for>` ở trên trỏ vào `inputId()` mà không
2739
+ * input nào mang id đó. Đo `/range-slider`: **4/8 tay trượt không có tên**
2740
+ * trong cây trợ năng (BUG-025).
2741
+ *
2742
+ * Người dùng bàn phím nghe "slider" trống trơn, không biết mình đang kéo
2743
+ * đầu nào của khoảng.
2744
+ */
2745
+ readonly minThumbId: _angular_core.Signal<string>;
2746
+ readonly maxThumbId: _angular_core.Signal<string>;
2747
+ /**
2748
+ * Tên của từng tay trượt: ghép nhãn chung với vai trò.
2749
+ *
2750
+ * Có `label()` thì ra "Khoảng giá — giá trị nhỏ nhất"; không có thì lùi về
2751
+ * `ariaLabel()` của nơi gọi, rồi mới tới tên trần. KHÔNG để trống: ở chế độ
2752
+ * dual, `<label for>` chỉ nối được MỘT trong hai tay trượt.
2753
+ */
2754
+ readonly ariaLabel: _angular_core.InputSignal<string>;
2755
+ readonly minThumbLabel: _angular_core.Signal<string>;
2756
+ readonly maxThumbLabel: _angular_core.Signal<string>;
2419
2757
  readonly describedBy: _angular_core.Signal<string | null>;
2420
2758
  readonly ariaInvalid: _angular_core.Signal<null>;
2421
2759
  readonly singlePercentage: _angular_core.Signal<number>;
@@ -2431,7 +2769,7 @@ declare class OlRange implements ControlValueAccessor {
2431
2769
  registerOnTouched(fn: any): void;
2432
2770
  setDisabledState(isDisabled: boolean): void;
2433
2771
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlRange, never>;
2434
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlRange, "ol-range", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "minValue": { "alias": "minValue"; "required": false; "isSignal": true; }; "maxValue": { "alias": "maxValue"; "required": false; "isSignal": true; }; "dualRange": { "alias": "dualRange"; "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; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "showValue": { "alias": "showValue"; "required": false; "isSignal": true; }; "showTooltip": { "alias": "showTooltip"; "required": false; "isSignal": true; }; "showMinMaxLabels": { "alias": "showMinMaxLabels"; "required": false; "isSignal": true; }; "unit": { "alias": "unit"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "minValue": "minValueChange"; "maxValue": "maxValueChange"; "disabled": "disabledChange"; }, never, ["[slot=prefix]", "[slot=suffix]"], true, never>;
2772
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlRange, "ol-range", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "minValue": { "alias": "minValue"; "required": false; "isSignal": true; }; "maxValue": { "alias": "maxValue"; "required": false; "isSignal": true; }; "dualRange": { "alias": "dualRange"; "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; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "showValue": { "alias": "showValue"; "required": false; "isSignal": true; }; "showTooltip": { "alias": "showTooltip"; "required": false; "isSignal": true; }; "showMinMaxLabels": { "alias": "showMinMaxLabels"; "required": false; "isSignal": true; }; "unit": { "alias": "unit"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "minValue": "minValueChange"; "maxValue": "maxValueChange"; "disabled": "disabledChange"; }, never, ["[slot=prefix]", "[slot=suffix]"], true, never>;
2435
2773
  }
2436
2774
 
2437
2775
  /**
@@ -2557,7 +2895,7 @@ declare class OlInputCounter implements ControlValueAccessor {
2557
2895
  private onChange;
2558
2896
  private onTouched;
2559
2897
  /** 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;
2898
+ private initialized;
2561
2899
  constructor();
2562
2900
  writeValue(val: number): void;
2563
2901
  registerOnChange(fn: (val: number) => void): void;
@@ -2626,7 +2964,7 @@ declare class OlOtpInput implements ControlValueAccessor {
2626
2964
  private onChange;
2627
2965
  private onTouched;
2628
2966
  /** 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;
2967
+ private initialized;
2630
2968
  constructor();
2631
2969
  writeValue(val: string): void;
2632
2970
  registerOnChange(fn: (val: string) => void): void;
@@ -2685,14 +3023,20 @@ declare class OlTagInput implements ControlValueAccessor {
2685
3023
  private onChange;
2686
3024
  private onTouched;
2687
3025
  /** Lần chạy đầu của `effect` không phải người dùng đổi — bỏ qua. */
2688
- private daKhoiTao;
3026
+ private initialized;
2689
3027
  constructor();
2690
3028
  writeValue(val: string[]): void;
2691
3029
  registerOnChange(fn: (val: string[]) => void): void;
2692
3030
  registerOnTouched(fn: () => void): void;
2693
3031
  setDisabledState(isDisabled: boolean): void;
2694
- /** Gọi khi rời ô nhập — form dùng để đánh dấu `touched`. */
2695
- protected danhDauCham(): void;
3032
+ /**
3033
+ * Gọi khi rời ô nhập — form dùng để đánh dấu `touched`.
3034
+ *
3035
+ * Trước đây hàm này KHÔNG được gọi ở đâu cả (template không có `(blur)`),
3036
+ * nên `onTouched()` chưa bao giờ chạy: Reactive Forms không đánh dấu control
3037
+ * là `touched`, và mọi kiểm tra dựa vào `ng-touched` im lặng không hoạt động.
3038
+ */
3039
+ protected markTouched(): void;
2696
3040
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlTagInput, never>;
2697
3041
  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>;
2698
3042
  }
@@ -2733,7 +3077,7 @@ declare class OlRichTextEditor implements ControlValueAccessor {
2733
3077
  private onChange;
2734
3078
  private onTouched;
2735
3079
  /** 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;
3080
+ private initialized;
2737
3081
  constructor();
2738
3082
  writeValue(val: string): void;
2739
3083
  registerOnChange(fn: (val: string) => void): void;
@@ -2762,6 +3106,11 @@ declare class OlTimepicker implements ControlValueAccessor {
2762
3106
  private readonly elementRef;
2763
3107
  readonly value: _angular_core.ModelSignal<string>;
2764
3108
  readonly label: _angular_core.InputSignal<string>;
3109
+ /**
3110
+ * Tên trợ năng khi KHÔNG có `label()` nhìn thấy được. Có `label()` thì
3111
+ * `aria-labelledby` đã lo — không cần truyền cái này.
3112
+ */
3113
+ readonly ariaLabel: _angular_core.InputSignal<string>;
2765
3114
  readonly placeholder: _angular_core.InputSignal<string>;
2766
3115
  readonly format: _angular_core.InputSignal<TimepickerFormat>;
2767
3116
  readonly size: _angular_core.InputSignal<TimepickerSize>;
@@ -2782,14 +3131,18 @@ declare class OlTimepicker implements ControlValueAccessor {
2782
3131
  /**
2783
3132
  * Đẩy bảng chọn giờ ra `<body>` thay vì để trong cây DOM của component.
2784
3133
  *
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`.
3134
+ * **Mặc định `'body'`** (từ bản này). Trước đây mặc định `null` — panel nằm
3135
+ * trong cây DOM của component, nên hễ đặt vào modal / drawer / bảng có
3136
+ * `overflow: hidden` là bị cắt, và người dùng phải tự biết mà bật. Đo
3137
+ * /modal: khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING
3138
+ * CONTEXT — z-index của lớp nổi không thoát ra được — và `overflow: hidden`
3139
+ * CẮT luôn phần tràn. Hai lỗi chồng nhau, z-index một mình không chữa nổi
3140
+ * cái thứ hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
2790
3141
  *
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.
3142
+ * Đặt `null` để quay về hành vi cũ: cần khi CSS của nơi dùng nhắm vào panel
3143
+ * qua cây DOM (`.form-cua-toi ol-select .panel`) — selector kiểu đó chết khi
3144
+ * panel treo ra ngoài. Cùng tên và cùng ý nghĩa với `appendTo` của ng-select
3145
+ * và PrimeNG.
2793
3146
  */
2794
3147
  readonly appendTo: _angular_core.InputSignal<"body" | null>;
2795
3148
  readonly showPresets: _angular_core.InputSignal<boolean>;
@@ -2799,14 +3152,19 @@ declare class OlTimepicker implements ControlValueAccessor {
2799
3152
  private readonly generatedId;
2800
3153
  protected readonly inputId: _angular_core.Signal<string>;
2801
3154
  readonly helperId: _angular_core.Signal<string>;
3155
+ /**
3156
+ * Id của `<label>`, để combobox trỏ vào bằng `aria-labelledby`. Cùng lỗi
3157
+ * với `ol-select`: `<label for>` không đặt tên cho `<div role="combobox">`
3158
+ * dù id khớp — đo `/timepicker` ra 39 node không tên (BUG-031).
3159
+ */
3160
+ readonly labelId: _angular_core.Signal<string>;
2802
3161
  readonly describedBy: _angular_core.Signal<string | null>;
2803
3162
  readonly ariaInvalid: _angular_core.Signal<"true" | null>;
2804
3163
  private readonly panel;
2805
3164
  private readonly overlayContainer;
2806
- private readonly positioning;
2807
3165
  /** Hàm trả panel về chỗ cũ + hàm ngắt theo dõi vị trí. */
2808
- private goPanel;
2809
- private ngatTheoDoi;
3166
+ private detachPanel;
3167
+ private stopTracking;
2810
3168
  readonly isOpen: _angular_core.WritableSignal<boolean>;
2811
3169
  readonly selectedHour: _angular_core.WritableSignal<number>;
2812
3170
  readonly selectedMinute: _angular_core.WritableSignal<number>;
@@ -2850,7 +3208,7 @@ declare class OlTimepicker implements ControlValueAccessor {
2850
3208
  registerOnTouched(fn: any): void;
2851
3209
  setDisabledState(isDisabled: boolean): void;
2852
3210
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlTimepicker, 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>;
3211
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTimepicker, "ol-timepicker", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "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>;
2854
3212
  }
2855
3213
 
2856
3214
  /**
@@ -2900,14 +3258,18 @@ declare class OlAutocomplete implements ControlValueAccessor {
2900
3258
  /**
2901
3259
  * Đẩy danh sách gợi ý ra `<body>` thay vì để trong cây DOM của component.
2902
3260
  *
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`.
3261
+ * **Mặc định `'body'`** (từ bản này). Trước đây mặc định `null` — panel nằm
3262
+ * trong cây DOM của component, nên hễ đặt vào modal / drawer / bảng có
3263
+ * `overflow: hidden` là bị cắt, và người dùng phải tự biết mà bật. Đo
3264
+ * /modal: khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING
3265
+ * CONTEXT — z-index của lớp nổi không thoát ra được — và `overflow: hidden`
3266
+ * CẮT luôn phần tràn. Hai lỗi chồng nhau, z-index một mình không chữa nổi
3267
+ * cái thứ hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
2908
3268
  *
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.
3269
+ * Đặt `null` để quay về hành vi cũ: cần khi CSS của nơi dùng nhắm vào panel
3270
+ * qua cây DOM (`.form-cua-toi ol-select .panel`) — selector kiểu đó chết khi
3271
+ * panel treo ra ngoài. Cùng tên và cùng ý nghĩa với `appendTo` của ng-select
3272
+ * và PrimeNG.
2911
3273
  */
2912
3274
  readonly appendTo: _angular_core.InputSignal<"body" | null>;
2913
3275
  readonly groupBy: _angular_core.InputSignal<string>;
@@ -2959,13 +3321,12 @@ declare class OlAutocomplete implements ControlValueAccessor {
2959
3321
  private onTouched;
2960
3322
  private readonly panel;
2961
3323
  private readonly overlayContainer;
2962
- private readonly positioning;
2963
3324
  /** Hàm trả panel về chỗ cũ + hàm ngắt theo dõi vị trí. */
2964
- private goPanel;
2965
- private ngatTheoDoi;
3325
+ private detachPanel;
3326
+ private stopTracking;
2966
3327
  private readonly destroyRef;
2967
3328
  /** Trạng thái mở ở lượt chạy trước — để `effect` chỉ phát khi ĐỔI. */
2968
- private daMo;
3329
+ private wasOpen;
2969
3330
  /** Hẹn giờ gỡ nảy cho `search`. */
2970
3331
  private hengio;
2971
3332
  constructor(elementRef: ElementRef);
@@ -2998,7 +3359,7 @@ declare class OlAutocomplete implements ControlValueAccessor {
2998
3359
  * một đường render thay vì hai nhánh song song.
2999
3360
  */
3000
3361
  readonly groupedOptions: _angular_core.Signal<{
3001
- ten: string;
3362
+ name: string;
3002
3363
  items: any[];
3003
3364
  }[]>;
3004
3365
  /**
@@ -3008,7 +3369,7 @@ declare class OlAutocomplete implements ControlValueAccessor {
3008
3369
  * nhóm: `activeIndex()` và `optionId(idx)` đều đánh theo chỉ số phẳng, lồng
3009
3370
  * vào thì `$index` reset mỗi nhóm và điều hướng bàn phím sai hết.
3010
3371
  */
3011
- tenNhomTaiVitri(idx: number): string | null;
3372
+ groupNameAt(idx: number): string | null;
3012
3373
  getItemLabel(item: any): string;
3013
3374
  getItemValue(item: any): any;
3014
3375
  isSelected(item: any): boolean;
@@ -3242,14 +3603,44 @@ declare class OlNavDropdown {
3242
3603
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlNavDropdown, "ol-nav-dropdown", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "alignRight": { "alias": "alignRight"; "required": false; "isSignal": true; }; }, {}, never, ["[slot=trigger], [trigger]", "*"], true, never>;
3243
3604
  }
3244
3605
 
3245
- type PaginationType = 'default' | 'arrows' | 'dropdown-buttons' | 'text-buttons' | 'two-buttons' | 'buttons-helper' | 'with-dropdown' | 'with-input' | 'simple-input';
3246
- type PaginationVariant = PaginationType | 'grouped' | 'separated';
3606
+ type PaginationType = 'default' | 'arrows' | 'dropdown-buttons' | 'text-buttons' | 'two-buttons' | 'buttons-helper' | 'with-dropdown' | 'with-input' | 'simple-input'
3607
+ /**
3608
+ * `grouped` và `separated` trước chỉ có ở `PaginationVariant`. Thêm vào đây
3609
+ * để người dùng chuyển từ `variant` (đã `@deprecated`) sang `type` mà không
3610
+ * mất giá trị nào — nếu không, bỏ `variant` là bỏ luôn hai bố cục này.
3611
+ *
3612
+ * Chúng là BÍ DANH bố cục, không phải bố cục mới: `grouped` ≡ `default`,
3613
+ * `separated` ≡ `text-buttons`. Xem `effectiveType`.
3614
+ */
3615
+ | 'grouped' | 'separated';
3616
+ /** @deprecated Dùng `PaginationType`. Nay hai kiểu đã trùng nhau hoàn toàn. */
3617
+ type PaginationVariant = PaginationType;
3247
3618
  declare class OlPagination {
3248
3619
  readonly type: _angular_core.InputSignal<PaginationType>;
3249
- readonly variant: _angular_core.InputSignal<PaginationVariant>;
3620
+ /**
3621
+ * @deprecated Dùng `type`. Giữ cho mã ngoài đang gọi — sẽ bỏ ở bản major sau.
3622
+ *
3623
+ * Hai input cho cùng MỘT trục là nguồn lỗi im lặng: người dùng truyền cả hai
3624
+ * rồi không biết cái nào thắng. `effectiveType` bên dưới xử lý tường minh
3625
+ * (`type` tường minh luôn thắng), nhưng bề mặt API vẫn nên còn một cái.
3626
+ *
3627
+ * `grouped`/`separated` chỉ có ở `variant` nên chúng đã được thêm vào
3628
+ * `PaginationType`: `grouped` ≡ `default`, `separated` ≡ `text-buttons`.
3629
+ *
3630
+ * KHÔNG xoá ngay: `@edmicro/olkit` đã publish lên npm công khai (1.1.1), bỏ
3631
+ * input công khai là breaking change cho consumer mà repo này không đo được.
3632
+ * Theo đúng nếp `@deprecated` đã có sẵn ở `onPageSizeChangeEvent` bên dưới.
3633
+ */
3634
+ readonly variant: _angular_core.InputSignal<PaginationType>;
3250
3635
  readonly total: _angular_core.InputSignal<number>;
3251
3636
  readonly pageSize: _angular_core.ModelSignal<number>;
3252
3637
  readonly pageIndex: _angular_core.ModelSignal<number>;
3638
+ /**
3639
+ * @deprecated Dùng `pageIndex`. Giữ cho mã ngoài đang gọi — bỏ ở major sau.
3640
+ *
3641
+ * Cùng lý do với `variant`: hai input một trục. Xem chú thích `trangHienTai`
3642
+ * bên dưới về cách gộp hai giá trị mà không xung đột.
3643
+ */
3253
3644
  readonly currentPage: _angular_core.ModelSignal<number>;
3254
3645
  readonly totalPagesInput: _angular_core.InputSignal<number | null>;
3255
3646
  readonly pageSizeOptions: _angular_core.InputSignal<number[]>;
@@ -3364,14 +3755,18 @@ declare class OlPopover {
3364
3755
  /**
3365
3756
  * Đẩy panel ra `<body>` thay vì để trong cây DOM của component.
3366
3757
  *
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`.
3758
+ * **Mặc định `'body'`** (từ bản này). Trước đây mặc định `null` — panel nằm
3759
+ * trong cây DOM của component, nên hễ đặt vào modal / drawer / bảng có
3760
+ * `overflow: hidden` là bị cắt, và người dùng phải tự biết mà bật. Đo
3761
+ * /modal: khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING
3762
+ * CONTEXT — z-index của lớp nổi không thoát ra được — và `overflow: hidden`
3763
+ * CẮT luôn phần tràn. Hai lỗi chồng nhau, z-index một mình không chữa nổi
3764
+ * cái thứ hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
3372
3765
  *
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.
3766
+ * Đặt `null` để quay về hành vi cũ: cần khi CSS của nơi dùng nhắm vào panel
3767
+ * qua cây DOM (`.form-cua-toi ol-select .panel`) — selector kiểu đó chết khi
3768
+ * panel treo ra ngoài. Cùng tên và cùng ý nghĩa với `appendTo` của ng-select
3769
+ * và PrimeNG.
3375
3770
  */
3376
3771
  readonly appendTo: _angular_core.InputSignal<"body" | null>;
3377
3772
  readonly placement: _angular_core.InputSignal<PopoverPlacement>;
@@ -3391,7 +3786,7 @@ declare class OlPopover {
3391
3786
  private readonly overlayContainer;
3392
3787
  private cleanupAutoUpdate;
3393
3788
  /** Hàm trả panel về chỗ cũ khi đóng. */
3394
- private goPanel;
3789
+ private detachPanel;
3395
3790
  constructor();
3396
3791
  toggle(): void;
3397
3792
  open(): void;
@@ -3475,6 +3870,140 @@ declare class OlProgress {
3475
3870
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlProgress, "ol-progress", never, { "progress": { "alias": "progress"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "labelPosition": { "alias": "labelPosition"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "showProgress": { "alias": "showProgress"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3476
3871
  }
3477
3872
 
3873
+ /**
3874
+ * Thanh tiến trình mảnh ở mép trên trang — kiểu nprogress / YouTube.
3875
+ *
3876
+ * Dùng cho việc **không biết trước bao lâu**: chuyển route, tải dữ liệu nền.
3877
+ * Khác `ol-progress` (thanh trong luồng, biết phần trăm chính xác) và khác
3878
+ * `ol-loading-overlay` (phủ và CHẶN thao tác).
3879
+ *
3880
+ * Là SERVICE chứ không phải component đặt trong template, theo nếp
3881
+ * `OlToastService`: nơi gọi thường là router guard hay HTTP interceptor, không
3882
+ * phải template.
3883
+ *
3884
+ * ```ts
3885
+ * // router guard
3886
+ * progressBar.start();
3887
+ * // ... điều hướng xong
3888
+ * progressBar.done();
3889
+ *
3890
+ * // HTTP interceptor — `done()` trong `finalize` để lỗi cũng tắt thanh
3891
+ * intercept(req: HttpRequest<unknown>, next: HttpHandler) {
3892
+ * this.pb.start();
3893
+ * return next.handle(req).pipe(finalize(() => this.pb.done()));
3894
+ * }
3895
+ * ```
3896
+ *
3897
+ * `<ol-progress-bar-top />` đặt một lần ở app root để vẽ.
3898
+ *
3899
+ * **Figma chưa vẽ component này** (node `3283:24173` chỉ có thanh trong luồng).
3900
+ * Đã ghi vào `FIGMA-CAN-VE-THEM.md`; dựng theo thông lệ phổ biến và dùng token
3901
+ * sẵn có, sẽ đối chiếu lại khi thiết kế vẽ xong.
3902
+ */
3903
+ declare class OlProgressBarService {
3904
+ /** Đếm số việc đang chạy, KHÔNG phải cờ bật/tắt. */
3905
+ private readonly activeCount;
3906
+ private readonly progressValue;
3907
+ private timer;
3908
+ /** Thanh có đang hiện. */
3909
+ readonly visible: _angular_core.Signal<boolean>;
3910
+ /** Phần trăm hiện tại (0–100). */
3911
+ readonly percent: _angular_core.Signal<number>;
3912
+ /**
3913
+ * Bắt đầu một việc.
3914
+ *
3915
+ * Đếm việc thay vì đặt cờ: hai lời gọi HTTP song song thì `done()` của cái
3916
+ * xong trước KHÔNG được tắt thanh khi cái kia còn chạy. Cờ boolean sẽ tắt
3917
+ * sớm và thanh nhấp nháy.
3918
+ */
3919
+ start(): void;
3920
+ /**
3921
+ * Kết thúc một việc. Chỉ khi việc CUỐI xong thì thanh mới chạy tới 100 và ẩn.
3922
+ */
3923
+ done(): void;
3924
+ /** Dừng mọi việc và ẩn ngay — dùng khi huỷ điều hướng. */
3925
+ reset(): void;
3926
+ private clearTimer;
3927
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlProgressBarService, never>;
3928
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<any>;
3929
+ }
3930
+
3931
+ /**
3932
+ * Vẽ thanh tiến trình mép trên trang. Đặt MỘT LẦN ở app root.
3933
+ *
3934
+ * ```html
3935
+ * <!-- app.html -->
3936
+ * <ol-progress-bar-top />
3937
+ * <router-outlet />
3938
+ * ```
3939
+ *
3940
+ * Trạng thái do `OlProgressBarService` giữ — component này chỉ đọc. Xem chú
3941
+ * thích đầy đủ ở service về lý do tách hai phần.
3942
+ *
3943
+ * `z-toast` (100) là bậc cao nhất trong thang z-index của lib: thanh này phải
3944
+ * thấy được cả khi modal (80) đang mở, vì điều hướng và tải nền vẫn xảy ra
3945
+ * trong lúc modal mở.
3946
+ */
3947
+ declare class OlProgressBarTop {
3948
+ readonly service: OlProgressBarService;
3949
+ /** Màu thanh. Mặc định `brand` theo Figma `ol-progress` (#3283:24173). */
3950
+ readonly color: _angular_core.InputSignal<"danger" | "brand" | "success" | "warning">;
3951
+ readonly barClasses: _angular_core.Signal<"bg-warning-surface" | "bg-danger-surface" | "bg-brand" | "bg-success">;
3952
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlProgressBarTop, never>;
3953
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlProgressBarTop, "ol-progress-bar-top", never, { "color": { "alias": "color"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3954
+ }
3955
+
3956
+ /**
3957
+ * Lớp phủ mờ + spinner, **CHẶN** thao tác trong lúc tải hoặc lưu.
3958
+ *
3959
+ * Khác ba thứ đã có trong lib:
3960
+ * - `ol-spinner` — vòng xoay đơn lẻ, không phủ, không chặn
3961
+ * - `ol-skeleton` — THAY CHỖ nội dung, không phủ lên
3962
+ * - `ol-table-loading` — skeleton hàng bảng, chỉ cho bảng
3963
+ *
3964
+ * Hai chế độ qua input `fullscreen`:
3965
+ *
3966
+ * ```html
3967
+ * <!-- phủ MỘT VÙNG: cha PHẢI có `relative` -->
3968
+ * <div class="relative">
3969
+ * <ol-loading-overlay [loading]="dangTai()" text="Đang tải..." />
3970
+ * <ol-table ... />
3971
+ * </div>
3972
+ *
3973
+ * <!-- phủ TOÀN MÀN -->
3974
+ * <ol-loading-overlay [loading]="dangLuu()" [fullscreen]="true" text="Đang lưu..." />
3975
+ * ```
3976
+ *
3977
+ * **Ràng buộc của chế độ phủ vùng**: phần tử cha phải có `position: relative`
3978
+ * (hoặc `absolute`/`fixed`). Thiếu nó thì `absolute inset-0` neo theo tổ tiên
3979
+ * được định vị gần nhất — có thể là cả trang. Đây là ràng buộc của CSS, lib
3980
+ * không tự thêm `relative` vào cha được vì không với tới phần tử đó.
3981
+ *
3982
+ * **Figma chưa vẽ component này** (node `3283:24173` chỉ có thanh tiến trình
3983
+ * trong luồng). Đã ghi vào `FIGMA-CAN-VE-THEM.md`; dựng bằng token sẵn có
3984
+ * (`bg-overlay-backdrop` mà modal/drawer/command đang dùng) và sẽ đối chiếu
3985
+ * lại khi thiết kế vẽ xong.
3986
+ */
3987
+ declare class OlLoadingOverlay {
3988
+ readonly loading: _angular_core.InputSignal<boolean>;
3989
+ /** Chữ dưới spinner. Rỗng thì chỉ hiện spinner. */
3990
+ readonly text: _angular_core.InputSignal<string>;
3991
+ /**
3992
+ * Phủ toàn màn thay vì phủ vùng cha.
3993
+ *
3994
+ * Phủ vùng (mặc định) dùng cho bảng đang tải, form đang lưu. Phủ toàn màn
3995
+ * dùng khi không được cho bấm bất cứ gì khác.
3996
+ */
3997
+ readonly fullscreen: _angular_core.InputSignal<boolean>;
3998
+ /** Theo nếp `ol-table-loading`: nhãn mặc định tiếng Việt. */
3999
+ readonly ariaLabel: _angular_core.InputSignal<string>;
4000
+ /** Spinner to hơn ở chế độ toàn màn cho cân với diện tích phủ. */
4001
+ readonly spinnerSize: _angular_core.Signal<"base" | "lg">;
4002
+ readonly wrapperClasses: _angular_core.Signal<"flex flex-col items-center justify-center gap-3 bg-overlay-backdrop backdrop-blur-xs fixed inset-0 z-modal" | "flex flex-col items-center justify-center gap-3 bg-overlay-backdrop backdrop-blur-xs absolute inset-0 z-raised">;
4003
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlLoadingOverlay, never>;
4004
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlLoadingOverlay, "ol-loading-overlay", never, { "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "text": { "alias": "text"; "required": false; "isSignal": true; }; "fullscreen": { "alias": "fullscreen"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
4005
+ }
4006
+
3478
4007
  /**
3479
4008
  * Trục `Type` của Figma `ol-rating` (#1149:82444). Code trước đây chỉ làm
3480
4009
  * được `Default`, hai kiểu kia Figma vẽ đủ mà code chưa có.
@@ -3498,6 +4027,16 @@ declare class OlRating {
3498
4027
  /** Nhãn hai đầu ở `radio-input` — Figma: "Không hài lòng" / "Rất hài lòng". */
3499
4028
  readonly minLabel: _angular_core.InputSignal<string>;
3500
4029
  readonly maxLabel: _angular_core.InputSignal<string>;
4030
+ /**
4031
+ * Tên trợ năng cho nhóm chấm điểm (`role="radiogroup"` ở nhánh
4032
+ * `radio-input`).
4033
+ *
4034
+ * Trước đây nhóm lấy tên từ `minLabel()` — sai ngữ nghĩa: đó là nhãn ĐẦU
4035
+ * THANG ("Rất tệ"), không phải tên của thang, và nó ĐÃ hiện thành chữ ngay
4036
+ * bên cạnh nên trình đọc màn hình đọc lặp. `minLabel()` giữ làm dự phòng để
4037
+ * mã đang chạy không mất tên khi chưa kịp đổi.
4038
+ */
4039
+ readonly ariaLabel: _angular_core.InputSignal<string>;
3501
4040
  /** Tên nhóm radio; tự sinh khi nơi gọi không truyền. */
3502
4041
  readonly name: _angular_core.InputSignal<string>;
3503
4042
  /** Giá trị đang chọn ở `radio-input`; `null` là chưa chọn. */
@@ -3532,7 +4071,7 @@ declare class OlRating {
3532
4071
  radioClass(v: number): string;
3533
4072
  select(v: number): void;
3534
4073
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlRating, never>;
3535
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlRating, "ol-rating", never, { "rating": { "alias": "rating"; "required": false; "isSignal": true; }; "max": { "alias": "max"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "score": { "alias": "score"; "required": false; "isSignal": true; }; "badgeText": { "alias": "badgeText"; "required": false; "isSignal": true; }; "minLabel": { "alias": "minLabel"; "required": false; "isSignal": true; }; "maxLabel": { "alias": "maxLabel"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; }, never, never, true, never>;
4074
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlRating, "ol-rating", never, { "rating": { "alias": "rating"; "required": false; "isSignal": true; }; "max": { "alias": "max"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "score": { "alias": "score"; "required": false; "isSignal": true; }; "badgeText": { "alias": "badgeText"; "required": false; "isSignal": true; }; "minLabel": { "alias": "minLabel"; "required": false; "isSignal": true; }; "maxLabel": { "alias": "maxLabel"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; }, never, never, true, never>;
3536
4075
  }
3537
4076
 
3538
4077
  declare class OlSidebar {
@@ -3792,7 +4331,7 @@ declare class OlSpinner {
3792
4331
  * - white: border-white
3793
4332
  * - success: border-emerald-600 (ngoài Figma)
3794
4333
  * - warning: border-yellow-500 (ngoài Figma)
3795
- * - danger: border-rose-600 (ngoài Figma)
4334
+ * - danger: text-fg-danger (token — trùng rose-600 ở chế độ sáng)
3796
4335
  * - gray: border-gray-400 (ngoài Figma)
3797
4336
  */
3798
4337
  readonly color: _angular_core.InputSignal<OlSpinnerColor>;
@@ -3937,6 +4476,23 @@ declare class OlTabItem {
3937
4476
  protected readonly hostClass: _angular_core.Signal<"contents" | "flex min-h-0 w-full flex-1 flex-col">;
3938
4477
  private readonly parentTabs;
3939
4478
  readonly isActive: _angular_core.Signal<boolean>;
4479
+ /**
4480
+ * Có dựng `role="tabpanel"` hay không (BUG-026).
4481
+ *
4482
+ * Ở chế độ `mobile` với các variant `default`/`pills`/`border-bottom`/
4483
+ * `underline`, `ol-tabs` render một `<select>` thay cho hàng tab — KHÔNG
4484
+ * sinh `role="tab"` nào. Panel vẫn khai `role="tabpanel"` +
4485
+ * `aria-labelledby="<tabId>"` thì nó thành **panel mồ côi**: vai trò đòi
4486
+ * phải có một `tab` điều khiển nó, mà `aria-labelledby` lại trỏ vào một id
4487
+ * KHÔNG TỒN TẠI trong DOM.
4488
+ *
4489
+ * ARIA hỏng tệ hơn không có ARIA: bộ đọc màn hình thông báo một cấu trúc
4490
+ * tab không hề tồn tại. Chế độ `<select>` đã tự có tên ("Chọn tab") nên bỏ
4491
+ * hẳn vai trò là đúng, không phải mất mát.
4492
+ *
4493
+ * Không có `parentTabs` (dùng `ol-tab-item` rời) thì giữ nguyên hành vi cũ.
4494
+ */
4495
+ readonly hasTabRole: _angular_core.Signal<boolean>;
3940
4496
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlTabItem, never>;
3941
4497
  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>;
3942
4498
  }
@@ -4345,6 +4901,20 @@ type StepperOrientation = 'horizontal' | 'vertical';
4345
4901
  type StepperBreakpoint = 'desktop' | 'mobile';
4346
4902
  declare class OlStepper {
4347
4903
  readonly currentStep: _angular_core.ModelSignal<number>;
4904
+ /**
4905
+ * Tên đọc được cho nút của một bước.
4906
+ *
4907
+ * Nút bước chỉ chiếu icon ở 3/4 nhánh (`completed` -> check, `error` ->
4908
+ * info-circle, có `icon` riêng -> icon đó); chỉ nhánh cuối mới có số thứ tự
4909
+ * làm chữ. `ol-icon` không tự sinh tên đọc được nên **bước càng hoàn thành
4910
+ * thì càng mất tên** — ngược hẳn mong đợi.
4911
+ *
4912
+ * Đo `/stepper` bằng accessibility tree: **39/61 nút câm** (BUG-013 mục 1).
4913
+ *
4914
+ * Nhãn gồm số thứ tự + tiêu đề (nếu có) + trạng thái, để người dùng trình
4915
+ * đọc màn hình biết đang ở đâu mà không phải đoán từ icon.
4916
+ */
4917
+ stepAriaLabel(i: number): string;
4348
4918
  readonly variant: _angular_core.InputSignal<StepperVariant>;
4349
4919
  readonly orientation: _angular_core.InputSignal<StepperOrientation>;
4350
4920
  readonly breakpoint: _angular_core.InputSignal<StepperBreakpoint>;
@@ -4380,6 +4950,22 @@ type OlTreeSelectionMode = 'single' | 'multiple';
4380
4950
  type OlTreeToggleIcon = 'chevron' | 'plus-minus';
4381
4951
  declare class OlTree {
4382
4952
  readonly nodes: _angular_core.ModelSignal<OlTreeNode[]>;
4953
+ /**
4954
+ * Template tuỳ biến cho nội dung mỗi hàng, khai bằng `#nodeTemplate`:
4955
+ *
4956
+ * ```html
4957
+ * <ol-tree [nodes]="nodes">
4958
+ * <ng-template #nodeTemplate let-node>
4959
+ * <ol-icon [name]="node.icon" />
4960
+ * <span class="truncate flex-1">{{ node.label }}</span>
4961
+ * </ng-template>
4962
+ * </ol-tree>
4963
+ * ```
4964
+ *
4965
+ * Theo đúng nếp `@ContentChild('optionTemplate')` của `ol-select`. Template
4966
+ * được truyền xuống MỌI cấp của cây đệ quy.
4967
+ */
4968
+ nodeTemplate?: TemplateRef<unknown>;
4383
4969
  readonly checkable: _angular_core.InputSignal<boolean>;
4384
4970
  readonly selectionMode: _angular_core.InputSignal<OlTreeSelectionMode>;
4385
4971
  readonly bordered: _angular_core.InputSignal<boolean>;
@@ -4413,7 +4999,7 @@ declare class OlTree {
4413
4999
  */
4414
5000
  handleKeydown(event: KeyboardEvent): void;
4415
5001
  /** Cha trực tiếp của một node, `null` nếu node ở mức gốc. */
4416
- private timCha;
5002
+ private findParent;
4417
5003
  handleNodeClick(node: OlTreeNode): void;
4418
5004
  handleNodeCheck(event: {
4419
5005
  node: OlTreeNode;
@@ -4426,13 +5012,26 @@ declare class OlTree {
4426
5012
  private setSelectedNodeSingle;
4427
5013
  private cloneTree;
4428
5014
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlTree, never>;
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>;
5015
+ 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"; }, ["nodeTemplate"], never, true, never>;
4430
5016
  }
4431
5017
 
4432
5018
  type OlTreeNodeSelectionMode = 'single' | 'multiple';
4433
5019
  type OlTreeNodeToggleIcon = 'chevron' | 'plus-minus';
4434
5020
  declare class OlTreeNodeItem {
4435
5021
  readonly node: _angular_core.InputSignal<OlTreeNode>;
5022
+ /**
5023
+ * Template tuỳ biến cho PHẦN NỘI DUNG của hàng (icon · nhãn · count · badge).
5024
+ *
5025
+ * Truyền qua `input` chứ không `inject` ngược cây: node vốn đã nhận 7 input
5026
+ * truyền xuống đệ quy, thêm một cái là đúng nếp sẵn có — còn `inject` ngược
5027
+ * là dựng thêm một cơ chế mới cho cùng một việc.
5028
+ *
5029
+ * Chỉ thay NỘI DUNG, KHÔNG thay cả hàng. Thẻ `role="treeitem"` cùng
5030
+ * `aria-level` / `aria-expanded` / roving tabindex và nút mở rộng, checkbox
5031
+ * đều do lib giữ. Cho người dùng vẽ cả hàng là mất ARIA vừa làm đúng chuẩn ở
5032
+ * BUG-017 — họ không có cách nào biết phải khai lại những thứ đó.
5033
+ */
5034
+ readonly nodeTemplate: _angular_core.InputSignal<TemplateRef<unknown> | null>;
4436
5035
  readonly checkable: _angular_core.InputSignal<boolean>;
4437
5036
  readonly selectionMode: _angular_core.InputSignal<OlTreeNodeSelectionMode>;
4438
5037
  readonly toggleIcon: _angular_core.InputSignal<OlTreeNodeToggleIcon>;
@@ -4476,7 +5075,7 @@ declare class OlTreeNodeItem {
4476
5075
  handleClick(): void;
4477
5076
  handleCheck(checked: boolean): void;
4478
5077
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlTreeNodeItem, 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>;
5078
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTreeNodeItem, "ol-tree-node", never, { "node": { "alias": "node"; "required": true; "isSignal": true; }; "nodeTemplate": { "alias": "nodeTemplate"; "required": false; "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>;
4480
5079
  }
4481
5080
 
4482
5081
  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';
@@ -4945,6 +5544,32 @@ declare class OlJumbotron {
4945
5544
  readonly subtitle: _angular_core.InputSignal<string>;
4946
5545
  readonly badgeText: _angular_core.InputSignal<string>;
4947
5546
  readonly variant: _angular_core.InputSignal<JumbotronVariant>;
5547
+ /**
5548
+ * Class cho badge — đổi theo `variant` vì NỀN đổi theo `variant`.
5549
+ *
5550
+ * Bản cũ khoá cứng `bg-white/10 … text-fg-yellow` cho mọi biến thể. Hợp lý
5551
+ * trên nền tối, nhưng `variant="light"` có nền gần trắng nên chữ vàng chỉ
5552
+ * đạt **1.58:1** — dưới xa ngưỡng WCAG 4.5 (BUG-013 mục 3b). Đo cả 5 badge
5553
+ * trên `/jumbotron`:
5554
+ *
5555
+ * | nền | tỷ lệ |
5556
+ * |------------------------|----------|
5557
+ * | `light` rgb(249,250,251) | **1.58** |
5558
+ * | `brand` rgb(43,65,130) | **4.45** (thiếu 0.05) |
5559
+ * | `dark` rgb(16,24,40) | 8.18 ✅ |
5560
+ *
5561
+ * `brand` cũng trượt (4.45, thiếu 0.05) — sát ngưỡng vẫn là trượt. Sửa bằng
5562
+ * cách làm nền badge TỐI hơn (`bg-white/5` thay `bg-white/10`) để giữ sắc
5563
+ * vàng, thay vì đổi màu chữ: đo `bg-white/5` ra **5.15**.
5564
+ *
5565
+ * Đã thử `text-fg-yellow-strong`, `text-yellow-200`, `text-yellow-100` —
5566
+ * cả ba là **class chết**, `getComputedStyle` trả về đen. `text-white` đạt
5567
+ * 7.3 nhưng mất sắc vàng của badge.
5568
+ *
5569
+ * Nền badge cũng phải đổi ở `light`: `bg-white/10` trên nền sáng gần như
5570
+ * vô hình.
5571
+ */
5572
+ readonly badgeClasses: _angular_core.Signal<"inline-flex items-center gap-2 px-3 py-1 rounded-full text-xs font-semibold border backdrop-blur-md bg-warning-soft border-warning-subtle text-fg-warning" | "inline-flex items-center gap-2 px-3 py-1 rounded-full text-xs font-semibold border backdrop-blur-md bg-white/5 border-white/20 text-fg-yellow">;
4948
5573
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlJumbotron, never>;
4949
5574
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlJumbotron, "ol-jumbotron", never, { "headingLevel": { "alias": "headingLevel"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "badgeText": { "alias": "badgeText"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; }, {}, never, ["[slot=badge-icon]", "[slot=actions]", "*"], true, never>;
4950
5575
  }
@@ -5065,6 +5690,17 @@ declare class OlFocusTrapService {
5065
5690
  private previousActiveElement;
5066
5691
  private trapElement;
5067
5692
  private focusableElements;
5693
+ /**
5694
+ * ĐẾM overlay đang mở, không dùng cờ bật/tắt.
5695
+ *
5696
+ * Mở modal rồi mở `ol-command` từ trong nó: đóng cái sau KHÔNG được nhả khoá
5697
+ * khi cái trước còn mở. Cờ boolean sẽ nhả sớm và nền cuộn lại giữa lúc modal
5698
+ * vẫn hiện — cùng lớp lỗi với `OlProgressBarService`.
5699
+ */
5700
+ private openOverlayCount;
5701
+ /** Giá trị `overflow`/`paddingRight` của `body` trước khi khoá, để trả lại. */
5702
+ private prevOverflow;
5703
+ private prevPaddingRight;
5068
5704
  /**
5069
5705
  * Selector cho các elements có thể focus được
5070
5706
  */
@@ -5079,6 +5715,33 @@ declare class OlFocusTrapService {
5079
5715
  * Release focus trap và restore focus về element trước đó
5080
5716
  */
5081
5717
  release(): void;
5718
+ /**
5719
+ * Khoá cuộn trang nền.
5720
+ *
5721
+ * `aria-modal="true"` là lời hứa với trợ năng rằng phần còn lại của trang
5722
+ * không tương tác được. Đo `/modal` trước khi sửa: overlay mở mà
5723
+ * `body.overflow` vẫn `visible`, lăn chuột làm nền trôi 1317 → 1717 trong
5724
+ * khi overlay đứng yên — người dùng mất ngữ cảnh (BUG-027).
5725
+ *
5726
+ * Khoá trên phần tử CUỘN THẬT (`document.scrollingElement`), KHÔNG mặc định
5727
+ * là `body`. Đo `/modal`: `scrollingElement` là `<html>` với
5728
+ * `scrollTop 1817`, còn `body.scrollTop` = 0 — đặt `overflow: hidden` trên
5729
+ * `body` thì nền vẫn cuộn y nguyên (đã thử: 1317 → 1717).
5730
+ *
5731
+ * Bù `padding-right` bằng đúng bề rộng thanh cuộn: đặt `overflow: hidden`
5732
+ * làm thanh cuộn dọc biến mất nên trang giật ngang vài px nếu không bù.
5733
+ *
5734
+ * KHÔNG dùng `position: fixed`: cách đó phải tự lưu và trả lại `scrollY`,
5735
+ * quên là trang nhảy về đầu khi đóng. `overflow: hidden` giữ nguyên vị trí.
5736
+ */
5737
+ private lockScroll;
5738
+ /**
5739
+ * Phần tử cuộn thật của trang. `document.scrollingElement` là `<html>` ở chế
5740
+ * độ chuẩn và `<body>` ở chế độ quirks — không đoán, hỏi trình duyệt.
5741
+ */
5742
+ private scrollingEl;
5743
+ /** Nhả khoá khi overlay CUỐI cùng đóng. */
5744
+ private unlockScroll;
5082
5745
  /**
5083
5746
  * Update danh sách focusable elements trong trap element
5084
5747
  */
@@ -5113,6 +5776,24 @@ interface PositioningOptions {
5113
5776
  shift?: boolean;
5114
5777
  arrow?: HTMLElement;
5115
5778
  middleware?: Middleware[];
5779
+ /**
5780
+ * Giới hạn kích thước floating element theo chỗ trống còn lại.
5781
+ *
5782
+ * Cần cho popup CAO (lịch, danh sách dài): `flip` chỉ lật sang phía đối diện
5783
+ * khi phía đó rộng hơn, nhưng nếu CẢ HAI phía đều không đủ thì popup vẫn tràn
5784
+ * khỏi màn. `size` cho nó một trần thật để cuộn bên trong.
5785
+ */
5786
+ size?: {
5787
+ /** Chiều cao tối thiểu, dưới mức này thì `flip` mới đổi phía. */
5788
+ minHeight?: number;
5789
+ /** Chừa mép màn. */
5790
+ padding?: number;
5791
+ /** Gọi khi có kích thước khả dụng mới. */
5792
+ apply?: (args: {
5793
+ availableHeight: number;
5794
+ availableWidth: number;
5795
+ }) => void;
5796
+ };
5116
5797
  }
5117
5798
  /**
5118
5799
  * Return type cho positioned coordinates
@@ -5213,23 +5894,44 @@ declare class OlOverlayContainer {
5213
5894
  * `--z-toast: 100`), nên lớp nổi trong container luôn phủ mọi thứ vẽ theo
5214
5895
  * thang đó. Cùng mốc CDK và PrimeNG dùng.
5215
5896
  */
5216
- private zHienTai;
5217
- /** Container chung, tạo một lần rồi dùng lại. */
5897
+ private currentZ;
5898
+ /**
5899
+ * Container chung, tạo một lần rồi dùng lại.
5900
+ *
5901
+ * Tìm trong DOM trước khi tạo mới. Service khai `providedIn: 'root'` nên mỗi
5902
+ * injector dựng một instance với `host = null` — trong ứng dụng thật chỉ có
5903
+ * một injector, nhưng mỗi `TestBed` là một cái. Không tìm lại thì container
5904
+ * chồng chất: đo được 4 test `select-append-to.spec.ts` đỏ 1 trên 3 lần
5905
+ * chạy, vì chúng giả định CHỈ MỘT container.
5906
+ *
5907
+ * Dùng lại cũng đúng hơn về mặt hành vi: hai instance service không nên vẽ
5908
+ * ra hai lớp phủ chồng nhau.
5909
+ */
5218
5910
  getContainer(): HTMLElement;
5219
5911
  /**
5220
5912
  * 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
5913
  * lớp mở sau luôn nằm trên, kể cả khi mở modal trong modal.
5222
5914
  */
5915
+ nextZIndex(): number;
5916
+ /**
5917
+ * @deprecated Dùng `nextZIndex()`. Giữ lại vì đã publish trong
5918
+ * `@edmicro/olkit@1.1.1` — bỏ phương thức công khai là breaking change.
5919
+ */
5223
5920
  capZIndex(): number;
5224
5921
  /**
5225
5922
  * Chuyển một phần tử vào container và cấp z-index.
5226
5923
  * Trả về hàm hoàn tác — gọi khi lớp đóng để trả phần tử về chỗ cũ.
5227
5924
  */
5925
+ attach(el: HTMLElement): () => void;
5926
+ /**
5927
+ * @deprecated Dùng `attach()`. Giữ lại vì đã publish trong
5928
+ * `@edmicro/olkit@1.1.1` — bỏ phương thức công khai là breaking change.
5929
+ */
5228
5930
  gan(el: HTMLElement): () => void;
5229
5931
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlOverlayContainer, never>;
5230
5932
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<any>;
5231
5933
  }
5232
5934
 
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 };
5935
+ 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, OlLoadingOverlay, OlModal, OlModalFooter, OlModalHeader, OlMonthView, OlNavDropdown, OlNavbar, OlNavbarItem, OlOtpInput, OlOverlayBase, OlOverlayContainer, OlPagination, OlPopover, OlPopoverContent, OlPositioningService, OlProgress, OlProgressBarService, OlProgressBarTop, 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, stripDiacritics };
5234
5936
  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 };
5235
5937
  //# sourceMappingURL=edmicro-olkit.d.ts.map