@edmicro/olkit 1.1.1 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { OnInit, AfterContentInit, OnDestroy, TemplateRef, ElementRef, QueryList, WritableSignal } from '@angular/core';
2
+ import { OnInit, AfterContentInit, OnDestroy, TemplateRef, ElementRef, QueryList } from '@angular/core';
3
3
  import { ControlValueAccessor } from '@angular/forms';
4
4
  import * as _edmicro_olkit from '@edmicro/olkit';
5
5
  import { SafeHtml } from '@angular/platform-browser';
@@ -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
  *
@@ -168,6 +174,7 @@ declare function boDau(chu: string): string;
168
174
  */
169
175
  declare class OlCommand {
170
176
  private readonly focusTrap;
177
+ private readonly overlayContainer;
171
178
  private readonly destroyRef;
172
179
  private readonly panel;
173
180
  private readonly searchInput;
@@ -258,6 +265,38 @@ declare class OlButton {
258
265
  * 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
266
  */
260
267
  readonly ariaLabel: _angular_core.InputSignal<string>;
268
+ /**
269
+ * Trạng thái mở/đóng của vùng mà nút này điều khiển.
270
+ *
271
+ * Cần vì đặt `[attr.aria-expanded]` thẳng lên `<ol-button>` **KHÔNG có tác
272
+ * dụng**: thuộc tính dừng ở thẻ bọc, không tới `<button>` bên trong — mà
273
+ * trình đọc màn hình đọc `<button>`. Đo `/collapse`: hai nút có
274
+ * `aria-expanded` trên host, `<button>` bên trong không có gì.
275
+ *
276
+ * Nhìn mã thì tưởng đã làm đúng trợ năng, nên đây là loại lỗi im lặng.
277
+ */
278
+ readonly ariaExpanded: _angular_core.InputSignal<boolean | null>;
279
+ /** `id` của vùng nội dung nút này điều khiển. Cùng lý do với `ariaExpanded`. */
280
+ readonly ariaControls: _angular_core.InputSignal<string>;
281
+ /** Trạng thái bật/tắt cho nút kiểu công tắc. Cùng lý do với `ariaExpanded`. */
282
+ readonly ariaPressed: _angular_core.InputSignal<boolean | null>;
283
+ /**
284
+ * Nội dung tooltip cho nút CHỈ-ICON, lấy từ `ariaLabel`.
285
+ *
286
+ * Đo 10 trang docs (BUG-018): **460 nút chỉ-icon, 0 nút câm** với trình đọc
287
+ * màn hình, nhưng **343 nút** có tên đọc được mà rê chuột không hiện gì.
288
+ * Chuẩn WAI-ARIA: nút chỉ-icon phải có nhãn NHÌN THẤY khi hover.
289
+ *
290
+ * Dùng `ariaLabel` sẵn có chứ không thêm input mới — cùng một thông tin.
291
+ *
292
+ * Chỉ bật cho `iconOnly`: nút có chữ đã tự hiện nghĩa, thêm tooltip lặp lại
293
+ * đúng dòng chữ đang nhìn là nhiễu.
294
+ *
295
+ * Rủi ro BUG-018 nêu ("chồng lên tooltip người dùng tự gắn") đã ĐO: hai
296
+ * directive lồng nhau, hover phần tử trong chỉ hiện MỘT tooltip vì
297
+ * `mouseenter` không nổi bọt. Xem `tooltip-nested.spec.ts`.
298
+ */
299
+ readonly tooltipText: _angular_core.Signal<string>;
261
300
  readonly btnClick: _angular_core.OutputEmitterRef<MouseEvent>;
262
301
  /**
263
302
  * Kích thước icon (px) theo bảng Figma ol-button.
@@ -268,7 +307,7 @@ declare class OlButton {
268
307
  readonly buttonClasses: _angular_core.Signal<string>;
269
308
  handleClick(event: MouseEvent): void;
270
309
  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>;
310
+ 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
311
  }
273
312
 
274
313
  type IconShapeSize = 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl';
@@ -310,6 +349,10 @@ type AlertDialogTone = 'danger' | 'warning' | 'brand';
310
349
  * Build vẫn xanh, chỉ bộ đọc màn hình là đọc sai.
311
350
  */
312
351
  declare class OlAlertDialog {
352
+ private readonly overlayContainer;
353
+ /** Hàm gỡ khỏi ngăn xếp Escape. `null` khi đang đóng. */
354
+ private releaseEscape;
355
+ constructor();
313
356
  readonly isOpen: _angular_core.ModelSignal<boolean>;
314
357
  /**
315
358
  * `heading` chứ không phải `title`: `title` là thuộc tính HTML có sẵn, đặt
@@ -334,7 +377,6 @@ declare class OlAlertDialog {
334
377
  * đòi `alertdialog` phải đóng được bằng Esc, nên xử lý ở đây, và cho nó rơi
335
378
  * vào nhánh AN TOÀN chứ không phải nhánh xác nhận.
336
379
  */
337
- onEscape(): void;
338
380
  onConfirm(): void;
339
381
  onCancel(): void;
340
382
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlAlertDialog, never>;
@@ -472,7 +514,19 @@ declare class OlAvatarGroup {
472
514
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlAvatarGroup, "ol-avatar-group", never, { "size": { "alias": "size"; "required": false; "isSignal": true; }; "counter": { "alias": "counter"; "required": false; "isSignal": true; }; "counterAriaLabel": { "alias": "counterAriaLabel"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
473
515
  }
474
516
 
475
- type BadgeTheme = 'brand' | 'gray' | 'white' | 'danger' | 'warning' | 'success' | 'default' | 'dark' | 'red' | 'yellow' | 'green';
517
+ /**
518
+ * Sáu màu CHUẨN (Figma `ol-badge` #1146:22250, trục `Theme`, 126 biến thể):
519
+ * Gray · White · Brand · Danger · Warning · Success.
520
+ *
521
+ * `ghost` là màu THỨ BẢY, CHƯA CÓ TRONG FIGMA — thêm 2026-09-23 theo yêu cầu,
522
+ * bê khuôn từ `ol-button` (nền trong suốt, chữ `text-heading`). Đừng gỡ khi
523
+ * đối chiếu Figma thấy thừa; còn nợ vẽ biến thể `Theme=Ghost`.
524
+ *
525
+ * Năm giá trị cuối là BÍ DANH, `normalizedColor()` gập về sáu màu chuẩn.
526
+ */
527
+ type BadgeColor = 'brand' | 'gray' | 'white' | 'danger' | 'warning' | 'success' | 'ghost' | 'default' | 'dark' | 'red' | 'yellow' | 'green';
528
+ /** @deprecated Tên cũ của `BadgeColor`. Giữ để không phá mã đang chạy. */
529
+ type BadgeTheme = BadgeColor;
476
530
  type BadgeSize = 'xs' | 'sm' | 'lg';
477
531
  type BadgeShape = 'square' | 'pill';
478
532
  /**
@@ -485,8 +539,21 @@ type BadgeShape = 'square' | 'pill';
485
539
  */
486
540
  type BadgeType = 'default' | 'dot' | 'avatar' | 'loader' | 'secondary';
487
541
  declare class OlBadge {
488
- readonly theme: _angular_core.InputSignal<BadgeTheme>;
489
- readonly color: _angular_core.InputSignal<BadgeTheme>;
542
+ /**
543
+ * Màu ngữ nghĩa của nhãn. Tên `color` cho khớp `ol-button` và 9 component
544
+ * khác trong thư viện — badge từng là chỗ duy nhất gọi trục này là `theme`.
545
+ */
546
+ readonly color: _angular_core.InputSignal<BadgeColor>;
547
+ /**
548
+ * @deprecated Dùng `color`.
549
+ *
550
+ * Bản cũ hoà giải hai input bằng cách SO VỚI GIÁ TRỊ MẶC ĐỊNH — `theme`
551
+ * thắng trừ khi nó bằng `'gray'` — nên `[color]="'brand'"` kèm
552
+ * `theme="danger"` thì `color` bị nuốt im lặng, không gì báo lỗi.
553
+ *
554
+ * Nay: cái nào được TRUYỀN thì thắng. Chuỗi rỗng là "không truyền".
555
+ */
556
+ readonly theme: _angular_core.InputSignal<"" | BadgeColor>;
490
557
  readonly size: _angular_core.InputSignal<BadgeSize>;
491
558
  readonly shape: _angular_core.InputSignal<BadgeShape>;
492
559
  readonly rounded: _angular_core.InputSignal<boolean>;
@@ -501,7 +568,7 @@ declare class OlBadge {
501
568
  readonly removable: _angular_core.InputSignal<boolean>;
502
569
  readonly href: _angular_core.InputSignal<string | null>;
503
570
  readonly dismiss: _angular_core.OutputEmitterRef<MouseEvent>;
504
- readonly normalizedTheme: _angular_core.Signal<string>;
571
+ readonly normalizedColor: _angular_core.Signal<string>;
505
572
  readonly isPill: _angular_core.Signal<boolean>;
506
573
  readonly badgeClasses: _angular_core.Signal<string>;
507
574
  /**
@@ -513,7 +580,7 @@ declare class OlBadge {
513
580
  readonly dotClasses: _angular_core.Signal<"bg-fg-brand-strong" | "bg-fg-danger-strong" | "bg-fg-warning" | "bg-success-strong" | "bg-heading">;
514
581
  onDismiss(event: MouseEvent): void;
515
582
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlBadge, never>;
516
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlBadge, "ol-badge", never, { "theme": { "alias": "theme"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "shape": { "alias": "shape"; "required": false; "isSignal": true; }; "rounded": { "alias": "rounded"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "dot": { "alias": "dot"; "required": false; "isSignal": true; }; "leftIcon": { "alias": "leftIcon"; "required": false; "isSignal": true; }; "rightIcon": { "alias": "rightIcon"; "required": false; "isSignal": true; }; "avatar": { "alias": "avatar"; "required": false; "isSignal": true; }; "avatarText": { "alias": "avatarText"; "required": false; "isSignal": true; }; "secondaryText": { "alias": "secondaryText"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "removable": { "alias": "removable"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; }, { "dismiss": "dismiss"; }, never, ["*"], true, never>;
583
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlBadge, "ol-badge", never, { "color": { "alias": "color"; "required": false; "isSignal": true; }; "theme": { "alias": "theme"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "shape": { "alias": "shape"; "required": false; "isSignal": true; }; "rounded": { "alias": "rounded"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "dot": { "alias": "dot"; "required": false; "isSignal": true; }; "leftIcon": { "alias": "leftIcon"; "required": false; "isSignal": true; }; "rightIcon": { "alias": "rightIcon"; "required": false; "isSignal": true; }; "avatar": { "alias": "avatar"; "required": false; "isSignal": true; }; "avatarText": { "alias": "avatarText"; "required": false; "isSignal": true; }; "secondaryText": { "alias": "secondaryText"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "removable": { "alias": "removable"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; }, { "dismiss": "dismiss"; }, never, ["*"], true, never>;
517
584
  }
518
585
 
519
586
  type BannerVariant = 'brand' | 'gradient' | 'warning' | 'dark' | 'subtle';
@@ -605,12 +672,28 @@ declare class OlBreadcrumbItem {
605
672
  readonly status: _angular_core.InputSignal<BreadcrumbItemStatus>;
606
673
  readonly showSeparator: _angular_core.InputSignal<boolean>;
607
674
  readonly separator: _angular_core.InputSignal<BreadcrumbSeparator | null>;
675
+ /**
676
+ * Chặng không bấm được — ví dụ cấp cha mà người dùng chưa có quyền xem.
677
+ *
678
+ * VƯỢT FIGMA CÓ CHỦ Ý (2026-09-23): `ol-breadcrumb-item` #1148:31348 chỉ vẽ
679
+ * ba Status — Initial · Hover · Active. Không có Disabled. Đây là mở rộng
680
+ * theo yêu cầu, không phải đọc sót thiết kế; đừng gỡ khi đối chiếu Figma.
681
+ *
682
+ * Khác `current`: `current` là trang ĐANG đứng (mục cuối, có `aria-current`),
683
+ * còn `disabled` là chặng có tồn tại nhưng không đi tới được.
684
+ */
685
+ readonly disabled: _angular_core.InputSignal<boolean>;
608
686
  private readonly parentBreadcrumb;
609
687
  readonly isCurrent: _angular_core.Signal<boolean>;
610
688
  readonly effectiveSeparator: _angular_core.Signal<BreadcrumbSeparator>;
689
+ /**
690
+ * `disabled` phải chặn ở ĐÂY, không chỉ đổi màu: còn sinh thẻ `<a href>` thì
691
+ * vẫn bấm được, vẫn Tab tới được, vẫn mở bằng chuột giữa — tô mờ chỉ làm
692
+ * người dùng tưởng nó hỏng.
693
+ */
611
694
  readonly isLink: _angular_core.Signal<boolean>;
612
695
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlBreadcrumbItem, never>;
613
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlBreadcrumbItem, "ol-breadcrumb-item", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "routerLink": { "alias": "routerLink"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "current": { "alias": "current"; "required": false; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; "status": { "alias": "status"; "required": false; "isSignal": true; }; "showSeparator": { "alias": "showSeparator"; "required": false; "isSignal": true; }; "separator": { "alias": "separator"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
696
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlBreadcrumbItem, "ol-breadcrumb-item", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "routerLink": { "alias": "routerLink"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "current": { "alias": "current"; "required": false; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; "status": { "alias": "status"; "required": false; "isSignal": true; }; "showSeparator": { "alias": "showSeparator"; "required": false; "isSignal": true; }; "separator": { "alias": "separator"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
614
697
  }
615
698
 
616
699
  type AppDevice = 'appstore' | 'googleplay' | 'google-play' | 'apple' | 'google';
@@ -661,7 +744,7 @@ declare class OlCard {
661
744
  * `flat` và `outline` đều được bảng API mô tả là "không bóng" — gộp một chỗ
662
745
  * để năm binding bóng không phải lặp lại điều kiện.
663
746
  */
664
- readonly khongBong: _angular_core.Signal<boolean>;
747
+ readonly noShadow: _angular_core.Signal<boolean>;
665
748
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlCard, never>;
666
749
  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
750
  }
@@ -733,22 +816,51 @@ declare class OlClipboard {
733
816
  readonly copied: _angular_core.WritableSignal<boolean>;
734
817
  /** Bao lâu thì nút quay lại biểu tượng sao chép, tính bằng mili giây. */
735
818
  readonly resetAfter: _angular_core.InputSignal<number>;
819
+ /**
820
+ * Tên đọc được cho nút. Nhánh "chưa sao chép" chỉ có SVG mang
821
+ * `aria-hidden="true"` nên nút HOÀN TOÀN CÂM với trình đọc màn hình — và đó
822
+ * là trạng thái người dùng thấy đầu tiên (BUG-013 mục 1b).
823
+ */
824
+ readonly ariaLabel: _angular_core.InputSignal<string>;
825
+ /** Chuỗi báo đã sao chép. Bản cũ khoá cứng tiếng Anh `Copied!`. */
826
+ readonly copiedText: _angular_core.InputSignal<string>;
736
827
  copy(): void;
737
828
  private markCopied;
738
829
  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>;
830
+ 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
831
  }
741
832
 
742
833
  declare class OlCollapse {
743
834
  readonly collapsed: _angular_core.ModelSignal<boolean>;
835
+ /**
836
+ * `id` của vùng nội dung, để nút điều khiển bên ngoài trỏ tới bằng
837
+ * `aria-controls`.
838
+ *
839
+ * `ol-collapse` CHỈ bọc nội dung — nút bấm do người dùng tự đặt bên ngoài
840
+ * (xem demo `/collapse`), nên lib không gắn `aria-expanded` thay họ được.
841
+ * Cái lib làm được là cho vùng một `id` ổn định:
842
+ *
843
+ * ```html
844
+ * <ol-button [attr.aria-expanded]="!gap()" [attr.aria-controls]="noiDung.contentId()">
845
+ * {{ gap() ? 'Mở ra' : 'Thu gọn' }}
846
+ * </ol-button>
847
+ * <ol-collapse #noiDung [(collapsed)]="gap">…</ol-collapse>
848
+ * ```
849
+ */
850
+ readonly contentId: _angular_core.InputSignal<string>;
744
851
  toggle(): void;
745
852
  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>;
853
+ 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
854
  }
748
855
 
749
856
  type DatepickerViewMode = 'day' | 'month' | 'year';
750
857
  type DatepickerSelectionMode = 'single' | 'range';
751
- type DatepickerSize = 'sm' | 'base' | 'lg';
858
+ /**
859
+ * Thang cỡ khớp `ol-input` (`sm | base | lg | xl`) để đặt cạnh nhau không so
860
+ * le. Đo được trước khi thêm `xl`: `ol-input size="xl"` cao 52px mà datepicker
861
+ * không có cỡ tương ứng.
862
+ */
863
+ type DatepickerSize = 'sm' | 'base' | 'lg' | 'xl';
752
864
  type DateRange = [Date | null, Date | null];
753
865
  interface DateCustomClass {
754
866
  date: Date;
@@ -864,14 +976,18 @@ declare class OlDatepicker implements ControlValueAccessor {
864
976
  /**
865
977
  * Đẩy khung lịch ra `<body>` thay vì để trong cây DOM của component.
866
978
  *
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`.
979
+ * **Mặc định `'body'`** (từ bản này). Trước đây mặc định `null` — panel nằm
980
+ * trong cây DOM của component, nên hễ đặt vào modal / drawer / bảng có
981
+ * `overflow: hidden` là bị cắt, và người dùng phải tự biết mà bật. Đo
982
+ * /modal: khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING
983
+ * CONTEXT — z-index của lớp nổi không thoát ra được — và `overflow: hidden`
984
+ * 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
985
+ * cái thứ hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
872
986
  *
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.
987
+ * Đặt `null` để quay về hành vi cũ: cần khi CSS của nơi dùng nhắm vào panel
988
+ * qua cây DOM (`.form-cua-toi ol-select .panel`) — selector kiểu đó chết khi
989
+ * panel treo ra ngoài. Cùng tên và cùng ý nghĩa với `appendTo` của ng-select
990
+ * và PrimeNG.
875
991
  */
876
992
  readonly appendTo: _angular_core.InputSignal<"body" | null>;
877
993
  readonly showFooter: _angular_core.InputSignal<boolean>;
@@ -884,17 +1000,55 @@ declare class OlDatepicker implements ControlValueAccessor {
884
1000
  readonly formattedChange: _angular_core.OutputEmitterRef<string>;
885
1001
  private readonly panel;
886
1002
  private readonly overlayContainer;
887
- private readonly positioning;
888
1003
  /** Hàm trả panel về chỗ cũ + hàm ngắt theo dõi vị trí. */
889
- private goPanel;
890
- private ngatTheoDoi;
1004
+ private detachPanel;
1005
+ /**
1006
+ * Hàm gỡ khỏi ngăn xếp Escape. `null` khi popup đang đóng.
1007
+ *
1008
+ * Xem `OlOverlayContainer.register` — chỉ lớp TRÊN CÙNG đóng khi nhấn
1009
+ * Escape, nên datepicker mở trong modal không còn kéo modal đóng theo.
1010
+ */
1011
+ private releaseEscape;
1012
+ /** Hàm gỡ theo dõi trần chiều cao popup. */
1013
+ private stopTrackingHeight;
891
1014
  readonly isOpen: _angular_core.WritableSignal<boolean>;
892
1015
  readonly inputValue: _angular_core.WritableSignal<string>;
1016
+ /**
1017
+ * Có gì để xoá hay không — dựa vào CHỮ TRONG Ô, không chỉ `value`.
1018
+ *
1019
+ * `value` chỉ được đặt khi chuỗi phân tích thành ngày hợp lệ (`if (parsed)`
1020
+ * trong `onInputChange`). Gõ dở "15/0" thì ô đã có chữ mà `value` vẫn rỗng,
1021
+ * nên nút xoá không hiện — đúng lúc người dùng gõ sai và cần xoá đi gõ lại.
1022
+ * Đo /datepicker: gõ "15/0" nút không hiện, gõ đủ "15/03/2026" mới hiện.
1023
+ */
1024
+ readonly hasContent: _angular_core.Signal<boolean>;
893
1025
  private onChange;
894
1026
  private onTouched;
895
1027
  private emitAllChanges;
896
1028
  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">;
1029
+ /**
1030
+ * Chiều cao khai bằng `h-*` CỐ ĐỊNH, không suy từ `py-*` + cỡ chữ.
1031
+ *
1032
+ * Bản cũ dùng `py-2 / py-2.5 / py-3.5` nên chiều cao phụ thuộc `line-height`
1033
+ * của cỡ chữ và không bao giờ khớp thang `h-*` của họ form. Đo trên DOM thật
1034
+ * (`/datepicker` và `/forms`, 8 ô nhập hiển thị):
1035
+ *
1036
+ * | cỡ | datepicker cũ | ol-input | lệch |
1037
+ * |------|---------------|----------|------|
1038
+ * | sm | 34px | 36px | −2 |
1039
+ * | base | 42px | 40px | +2 |
1040
+ * | lg | 54px | 48px | +6 |
1041
+ *
1042
+ * Đặt cạnh nhau trong một hàng là thấy so le ngay, nặng nhất ở `lg`.
1043
+ *
1044
+ * Thang `9/10/12` là chuẩn CHUNG của họ form, không phải chép riêng của
1045
+ * `ol-input`: `ol-timepicker` dùng `h-9/h-10/h-12`, `ol-select` dùng
1046
+ * `min-h-9/min-h-10/min-h-12`. Datepicker là component DUY NHẤT lạc thang.
1047
+ *
1048
+ * Bỏ `py-*`: chiều cao đã cố định thì đệm dọc chỉ ép chữ, giữ `px-*` cho đệm
1049
+ * ngang và `ps-*` cho chỗ icon lịch.
1050
+ */
1051
+ readonly inputSizeClasses: _angular_core.Signal<string>;
898
1052
  /**
899
1053
  * Cỡ icon lịch theo `size` của ô nhập.
900
1054
  *
@@ -921,7 +1075,6 @@ declare class OlDatepicker implements ControlValueAccessor {
921
1075
  /** Ngắt theo dõi vị trí và trả panel về chỗ cũ. Gọi được nhiều lần. */
922
1076
  private donDepPanel;
923
1077
  onDocumentClick(event: MouseEvent): void;
924
- onEscape(): void;
925
1078
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlDatepicker, never>;
926
1079
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDatepicker, "ol-datepicker", never, { "id": { "alias": "id"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "format": { "alias": "format"; "required": false; "isSignal": true; }; "minDate": { "alias": "minDate"; "required": false; "isSignal": true; }; "maxDate": { "alias": "maxDate"; "required": false; "isSignal": true; }; "daysDisabled": { "alias": "daysDisabled"; "required": false; "isSignal": true; }; "datesDisabled": { "alias": "datesDisabled"; "required": false; "isSignal": true; }; "datesEnabled": { "alias": "datesEnabled"; "required": false; "isSignal": true; }; "appendTo": { "alias": "appendTo"; "required": false; "isSignal": true; }; "showFooter": { "alias": "showFooter"; "required": false; "isSignal": true; }; "inline": { "alias": "inline"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; "dateChange": "dateChange"; "timestampChange": "timestampChange"; "isoChange": "isoChange"; "formattedChange": "formattedChange"; }, never, never, true, never>;
927
1080
  }
@@ -956,14 +1109,18 @@ declare class OlDateRangePicker implements ControlValueAccessor {
956
1109
  /**
957
1110
  * Đẩy khung lịch ra `<body>` thay vì để trong cây DOM của component.
958
1111
  *
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`.
1112
+ * **Mặc định `'body'`** (từ bản này). Trước đây mặc định `null` — panel nằm
1113
+ * trong cây DOM của component, nên hễ đặt vào modal / drawer / bảng có
1114
+ * `overflow: hidden` là bị cắt, và người dùng phải tự biết mà bật. Đo
1115
+ * /modal: khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING
1116
+ * CONTEXT — z-index của lớp nổi không thoát ra được — và `overflow: hidden`
1117
+ * 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
1118
+ * cái thứ hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
964
1119
  *
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.
1120
+ * Đặt `null` để quay về hành vi cũ: cần khi CSS của nơi dùng nhắm vào panel
1121
+ * qua cây DOM (`.form-cua-toi ol-select .panel`) — selector kiểu đó chết khi
1122
+ * panel treo ra ngoài. Cùng tên và cùng ý nghĩa với `appendTo` của ng-select
1123
+ * và PrimeNG.
967
1124
  */
968
1125
  readonly appendTo: _angular_core.InputSignal<"body" | null>;
969
1126
  readonly showFooter: _angular_core.InputSignal<boolean>;
@@ -974,13 +1131,20 @@ declare class OlDateRangePicker implements ControlValueAccessor {
974
1131
  readonly rangeChange: _angular_core.OutputEmitterRef<DateRange | null>;
975
1132
  private readonly panel;
976
1133
  private readonly overlayContainer;
977
- private readonly positioning;
978
1134
  /** Hàm trả panel về chỗ cũ + hàm ngắt theo dõi vị trí. */
979
- private goPanel;
980
- private ngatTheoDoi;
1135
+ private detachPanel;
1136
+ /** Hàm gỡ khỏi ngăn xếp Escape. `null` khi đang đóng. */
1137
+ private releaseEscape;
1138
+ /** Hàm gỡ theo dõi trần chiều cao popup. */
1139
+ private stopTrackingHeight;
981
1140
  readonly isOpen: _angular_core.WritableSignal<boolean>;
982
1141
  readonly startInputValue: _angular_core.WritableSignal<string>;
983
1142
  readonly endInputValue: _angular_core.WritableSignal<string>;
1143
+ /**
1144
+ * Có gì để xoá hay không — dựa vào CHỮ TRONG Ô, không chỉ `value`.
1145
+ * Cùng lý do với `ol-datepicker`: `value` chỉ đặt khi chuỗi phân tích được.
1146
+ */
1147
+ readonly hasContent: _angular_core.Signal<boolean>;
984
1148
  private onChange;
985
1149
  private onTouched;
986
1150
  constructor();
@@ -990,7 +1154,30 @@ declare class OlDateRangePicker implements ControlValueAccessor {
990
1154
  * trong khi ô nhập co giãn theo `inputSizeClasses()`.
991
1155
  */
992
1156
  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">;
1157
+ /**
1158
+ * Chiều cao `h-*` cố định — cùng luật với `ol-datepicker`, xem chú thích đầy
1159
+ * đủ ở đó. Tài liệu BUG-019 chỉ nêu `ol-datepicker`, nhưng component này
1160
+ * 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à
1161
+ * để lại đúng lỗi đó ở chỗ khác.
1162
+ */
1163
+ readonly inputSizeClasses: _angular_core.Signal<string>;
1164
+ /**
1165
+ * Cỡ cho biến thể `joined` — khung bọc mang viền, hai input bên trong trong
1166
+ * suốt. Phải tách khỏi `inputSizeClasses()`: ở đây chiều cao thuộc về KHUNG,
1167
+ * không thuộc về input.
1168
+ *
1169
+ * Trước đây nhánh `joined` khoá cứng `text-sm py-2` nên `[size]` không có
1170
+ * tác dụng gì. Đo /datepicker: `dual-inputs` ra 40px theo `h-10`, còn
1171
+ * `joined` luôn **36px** ở mọi cỡ — suy từ `py-2` + cỡ chữ, không theo thang
1172
+ * `h-*` của họ form. Đúng lỗi BUG-019 đã sửa cho `ol-datepicker`, sót lại ở
1173
+ * nhánh này.
1174
+ *
1175
+ * Khung có `p-1` = 4px mỗi cạnh, nên input con lùi 8px để TỔNG chiều cao
1176
+ * khớp thang: h-9 → h-7, h-10 → h-8, h-12 → h-10, h-13 → h-11.
1177
+ */
1178
+ readonly joinedWrapperClasses: _angular_core.Signal<"h-9" | "h-12" | "h-10" | "h-13">;
1179
+ /** Input bên trong khung `joined` — trừ đi `p-1` của khung. */
1180
+ readonly joinedInputClasses: _angular_core.Signal<"h-7 text-xs" | "h-10 text-base" | "h-11 text-base" | "h-8 text-sm">;
994
1181
  writeValue(val: DateRange | null): void;
995
1182
  registerOnChange(fn: (val: DateRange | null) => void): void;
996
1183
  registerOnTouched(fn: () => void): void;
@@ -1007,7 +1194,6 @@ declare class OlDateRangePicker implements ControlValueAccessor {
1007
1194
  /** Ngắt theo dõi vị trí và trả panel về chỗ cũ. Gọi được nhiều lần. */
1008
1195
  private donDepPanel;
1009
1196
  onDocumentClick(event: MouseEvent): void;
1010
- onEscape(): void;
1011
1197
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlDateRangePicker, never>;
1012
1198
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDateRangePicker, "ol-date-range-picker", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "startPlaceholder": { "alias": "startPlaceholder"; "required": false; "isSignal": true; }; "endPlaceholder": { "alias": "endPlaceholder"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "fromLabel": { "alias": "fromLabel"; "required": false; "isSignal": true; }; "toLabel": { "alias": "toLabel"; "required": false; "isSignal": true; }; "showInfoIcon": { "alias": "showInfoIcon"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "dualMonth": { "alias": "dualMonth"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "format": { "alias": "format"; "required": false; "isSignal": true; }; "minDate": { "alias": "minDate"; "required": false; "isSignal": true; }; "maxDate": { "alias": "maxDate"; "required": false; "isSignal": true; }; "daysDisabled": { "alias": "daysDisabled"; "required": false; "isSignal": true; }; "datesDisabled": { "alias": "datesDisabled"; "required": false; "isSignal": true; }; "datesEnabled": { "alias": "datesEnabled"; "required": false; "isSignal": true; }; "appendTo": { "alias": "appendTo"; "required": false; "isSignal": true; }; "showFooter": { "alias": "showFooter"; "required": false; "isSignal": true; }; "presets": { "alias": "presets"; "required": false; "isSignal": true; }; "inline": { "alias": "inline"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; "rangeChange": "rangeChange"; }, never, never, true, never>;
1013
1199
  }
@@ -1040,6 +1226,18 @@ declare class OlDatepickerHeader {
1040
1226
  declare class OlDayView {
1041
1227
  readonly state: OlDatepickerStateService;
1042
1228
  readonly customMatrix: _angular_core.InputSignal<DayCell[][] | null>;
1229
+ /**
1230
+ * Ghim chiều cao vùng lưới theo 6 hàng.
1231
+ *
1232
+ * Dùng khi popup hiện HAI tháng cạnh nhau: số hàng mỗi tháng khác nhau (4, 5
1233
+ * 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
1234
+ * 7+8/2025: tháng 7 cao 224px (5 hàng), tháng 8 cao 264px (6 hàng) — **lệch
1235
+ * 40px**.
1236
+ *
1237
+ * 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
1238
+ * lỗi vừa sửa (dòng số mờ vô nghĩa dưới đáy lịch).
1239
+ */
1240
+ readonly fixedRows: _angular_core.InputSignal<boolean>;
1043
1241
  readonly daySelect: _angular_core.OutputEmitterRef<Date>;
1044
1242
  /** Nhãn thứ tiếng Việt theo Figma #1149:51169: CN · T2…T7. */
1045
1243
  readonly weekdays: string[];
@@ -1048,7 +1246,7 @@ declare class OlDayView {
1048
1246
  onMouseLeave(): void;
1049
1247
  getCellClasses(cell: DayCell): string[];
1050
1248
  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>;
1249
+ 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
1250
  }
1053
1251
 
1054
1252
  declare class OlMonthView {
@@ -1089,6 +1287,67 @@ declare class OlDatepickerPopup {
1089
1287
  readonly showFooter: _angular_core.InputSignal<boolean>;
1090
1288
  readonly dualMonth: _angular_core.InputSignal<boolean>;
1091
1289
  readonly presets: _angular_core.InputSignal<DatepickerPreset[]>;
1290
+ /**
1291
+ * Lịch NHÚNG nằm trong luồng trang, không phải lớp nổi.
1292
+ *
1293
+ * Trần chiều cao và `overflow-y: auto` chỉ dành cho popup nổi — áp cho lịch
1294
+ * nhúng là sai: nó ăn giá trị dự phòng `85svh` dù không ai đặt biến. Đo ở
1295
+ * 414×500: lịch nhúng **bị cắt 56px**, footer chứa nút "Xác nhận" nằm NGOÀI
1296
+ * khung, và nó mọc thanh cuộn riêng trong khi người dùng đáng lẽ chỉ cần cuộn
1297
+ * trang như mọi nội dung khác.
1298
+ */
1299
+ readonly inline: _angular_core.InputSignal<boolean>;
1300
+ /**
1301
+ * Bề rộng màn tối thiểu để hiện hai lịch: hai lịch `w-63` (252px) + khe 24px
1302
+ * + đệm 40px ≈ 570, làm tròn lên 600 cho chắc.
1303
+ */
1304
+ private static readonly MIN_WIDTH_TWO_MONTHS;
1305
+ /**
1306
+ * Bề rộng MÀN HÌNH.
1307
+ *
1308
+ * Đã thử đo popup và đo `#panel` bằng ResizeObserver — cả hai đều SAI vì
1309
+ * chúng `absolute`/co theo nội dung, tạo vòng lặp phản hồi: hai lịch xuống
1310
+ * 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:
1311
+ * ở 1440px, đo popup ra 482 (vẫn 2 tháng xếp dọc), đo panel ra 440 (tụt về 1
1312
+ * tháng dù màn rất rộng).
1313
+ *
1314
+ * Màn hình là thước ỔN ĐỊNH duy nhất ở đây — nó không phụ thuộc nội dung.
1315
+ * Phần "chỗ thật còn lại bao nhiêu" vẫn do `flex-wrap` trong template lo, như
1316
+ * chú thích ở đó đã nêu; hai cơ chế bổ sung nhau chứ không chống nhau:
1317
+ * 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
1318
+ * `flex-wrap` lo ca khung cha hẹp trên desktop.
1319
+ */
1320
+ private readonly screenWidth;
1321
+ /**
1322
+ * `dualMonth` là ý định của người dùng, `showTwoMonths` là thứ VẼ ĐƯỢC.
1323
+ *
1324
+ * Đo ở 375×812: hai lịch + footer cao 690px trong khi chỗ trống dưới ô nhập
1325
+ * chỉ ~320px — **49/70 ô ngày và toàn bộ nút "Xác nhận" nằm ngoài màn**,
1326
+ * 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
1327
+ * bằng mũi tên vẫn đủ.
1328
+ */
1329
+ /**
1330
+ * Trần chiều cao + cuộn CHỈ cho popup nổi.
1331
+ *
1332
+ * Dùng `[class]` trả chuỗi chứ KHÔNG `[class.max-h-…]` với giá trị tuỳ ý: tên class
1333
+ * chứa dấu chấm và ngoặc vuông nên Angular cắt hỏng tên khi dùng class
1334
+ * binding từng cái (`[class.h-1.5]` ra `h-1`).
1335
+ */
1336
+ readonly wrapperClasses: _angular_core.Signal<"" | "max-h-[var(--ol-dp-max-height,85svh)] overflow-y-auto overscroll-contain">;
1337
+ /**
1338
+ * Trần cho KHỐI LỊCH, để footer ghim lại còn vùng ngày cuộn.
1339
+ *
1340
+ * `wrapperClasses` đặt trần cho cả popup nên footer cuộn theo — đo
1341
+ * /datepicker ở 1278px: footer cao 49px, đáy ở 383 trong khung 350, tràn
1342
+ * 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
1343
+ * `[lịch + footer]`, còn `overflow` nằm ở riêng vùng lịch bên trong.
1344
+ *
1345
+ * Trừ đi `p-3.5`/`sm:p-5` của wrapper: dùng `calc` thay số cứng vì đệm đổi
1346
+ * theo breakpoint.
1347
+ */
1348
+ readonly calendarBlockClasses: _angular_core.Signal<"" | "max-h-[calc(var(--ol-dp-max-height,85svh)-2rem)] min-h-0">;
1349
+ readonly showTwoMonths: _angular_core.Signal<boolean>;
1350
+ constructor();
1092
1351
  readonly valueChange: _angular_core.OutputEmitterRef<Date | [Date | null, Date | null]>;
1093
1352
  readonly clearClick: _angular_core.OutputEmitterRef<void>;
1094
1353
  readonly applyClick: _angular_core.OutputEmitterRef<void>;
@@ -1100,7 +1359,7 @@ declare class OlDatepickerPopup {
1100
1359
  onFooterCancel(): void;
1101
1360
  applyPreset(preset: DatepickerPreset): void;
1102
1361
  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>;
1362
+ 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
1363
  }
1105
1364
 
1106
1365
  /**
@@ -1125,7 +1384,16 @@ declare function isDateDisabled(date: Date, minDate?: Date | null, maxDate?: Dat
1125
1384
  declare function formatDate(date: Date | null | undefined, format?: string): string;
1126
1385
  declare function parseDate(dateStr: string, format?: string): Date | null;
1127
1386
  /**
1128
- * Generates 42 days grid (6 weeks x 7 days) for the calendar view
1387
+ * 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
1388
+ * ngày nào của tháng đang xem.
1389
+ *
1390
+ * Vì sao: bản cũ luôn trả cứng 6 hàng. Đo 84 tháng (2024–2030) thì **66 tháng
1391
+ * (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
1392
+ * 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ố.
1393
+ *
1394
+ * 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
1395
+ * `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.
1396
+ * Đ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
1397
  */
1130
1398
  declare function generateMonthMatrix(viewDate: Date, options?: {
1131
1399
  selectedDate?: Date | null;
@@ -1153,6 +1421,7 @@ type DrawerPlacement = 'left' | 'right' | 'top' | 'bottom';
1153
1421
  type DrawerSize = 'sm' | 'base' | 'lg' | 'xl' | 'full';
1154
1422
  declare class OlDrawer {
1155
1423
  private readonly focusTrap;
1424
+ private readonly overlayContainer;
1156
1425
  private readonly destroyRef;
1157
1426
  private readonly panel;
1158
1427
  readonly isOpen: _angular_core.ModelSignal<boolean>;
@@ -1204,11 +1473,18 @@ declare class OlDrawerHeader {
1204
1473
  */
1205
1474
  readonly headingLevel: _angular_core.InputSignal<1 | 2 | 3 | 4 | 5 | 6>;
1206
1475
  readonly heading: _angular_core.InputSignal<string>;
1476
+ /** Icon đầu tiêu đề. Rỗng thì KHÔNG dựng khung. Xem `ol-modal-header`. */
1477
+ readonly icon: _angular_core.InputSignal<string>;
1478
+ /** Mô tả phụ dưới tiêu đề. Cùng tên với `ol-modal-header`. */
1479
+ readonly description: _angular_core.InputSignal<string>;
1480
+ /** @deprecated Dùng `description`. Giữ để không phá mã đang chạy. */
1207
1481
  readonly helperText: _angular_core.InputSignal<string>;
1482
+ /** Cái nào được TRUYỀN thì thắng. Xem chú thích ở `ol-modal-header`. */
1483
+ readonly descriptionText: _angular_core.Signal<string>;
1208
1484
  readonly showClose: _angular_core.InputSignal<boolean>;
1209
1485
  readonly closeClick: _angular_core.OutputEmitterRef<void>;
1210
1486
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlDrawerHeader, never>;
1211
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDrawerHeader, "ol-drawer-header", never, { "headingLevel": { "alias": "headingLevel"; "required": false; "isSignal": true; }; "heading": { "alias": "heading"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "showClose": { "alias": "showClose"; "required": false; "isSignal": true; }; }, { "closeClick": "closeClick"; }, never, ["*"], true, never>;
1487
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlDrawerHeader, "ol-drawer-header", never, { "headingLevel": { "alias": "headingLevel"; "required": false; "isSignal": true; }; "heading": { "alias": "heading"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "showClose": { "alias": "showClose"; "required": false; "isSignal": true; }; }, { "closeClick": "closeClick"; }, never, ["*"], true, never>;
1212
1488
  }
1213
1489
 
1214
1490
  /** Khớp trục `Type` của Figma ol-drawer-footer (node 1149:37232). */
@@ -1247,20 +1523,26 @@ declare class OlDropdown {
1247
1523
  private readonly overlayContainer;
1248
1524
  private cleanupAutoUpdate;
1249
1525
  /** Hàm trả menu về chỗ cũ khi đóng. */
1250
- private goMenu;
1526
+ private detachMenu;
1527
+ /** Hàm gỡ khỏi ngăn xếp Escape. `null` khi đang đóng. */
1528
+ private releaseEscape;
1251
1529
  readonly label: _angular_core.InputSignal<string | null>;
1252
1530
  readonly isOpen: _angular_core.ModelSignal<boolean>;
1253
1531
  /**
1254
1532
  * Đẩy menu ra `<body>` thay vì để trong cây DOM của component.
1255
1533
  *
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`.
1534
+ * **Mặc định `'body'`** (từ bản này). Trước đây mặc định `null` — panel nằm
1535
+ * trong cây DOM của component, nên hễ đặt vào modal / drawer / bảng có
1536
+ * `overflow: hidden` là bị cắt, và người dùng phải tự biết mà bật. Đo
1537
+ * /modal: khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING
1538
+ * CONTEXT — z-index của lớp nổi không thoát ra được — và `overflow: hidden`
1539
+ * 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
1540
+ * cái thứ hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
1261
1541
  *
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.
1542
+ * Đặt `null` để quay về hành vi cũ: cần khi CSS của nơi dùng nhắm vào panel
1543
+ * qua cây DOM (`.form-cua-toi ol-select .panel`) — selector kiểu đó chết khi
1544
+ * panel treo ra ngoài. Cùng tên và cùng ý nghĩa với `appendTo` của ng-select
1545
+ * và PrimeNG.
1264
1546
  */
1265
1547
  readonly appendTo: _angular_core.InputSignal<"body" | null>;
1266
1548
  readonly placement: _angular_core.InputSignal<DropdownPlacement>;
@@ -1283,7 +1565,6 @@ declare class OlDropdown {
1283
1565
  private anchorElement;
1284
1566
  private stopAutoUpdate;
1285
1567
  onDocumentClick(event: MouseEvent): void;
1286
- onEscape(): void;
1287
1568
  /** Toạ độ con trỏ lần bấm chuột phải gần nhất, đổ vào mốc ẩn. */
1288
1569
  readonly cursorX: _angular_core.WritableSignal<number>;
1289
1570
  readonly cursorY: _angular_core.WritableSignal<number>;
@@ -1565,7 +1846,28 @@ declare class OlCheckboxRadioForm implements ControlValueAccessor {
1565
1846
  /** Tên nhóm cho ô tròn — cùng `name` thì chỉ chọn được một. */
1566
1847
  readonly name: _angular_core.InputSignal<string>;
1567
1848
  readonly value: _angular_core.InputSignal<string>;
1568
- readonly inputId: _angular_core.InputSignal<string>;
1849
+ /**
1850
+ * Id của ô nhập, để `<label for>` nối được.
1851
+ *
1852
+ * TỰ SINH khi nơi gọi không truyền. Mặc định RỖNG trước đây làm
1853
+ * `[attr.id]="inputId() || null"` ra `null`, nên `<ol-label [for]="">` trỏ
1854
+ * vào hư không: đo `/checkbox-radio` ra **29 control không tên** trong cây
1855
+ * trợ năng dù nhãn hiển thị đẹp trên màn hình (BUG-031).
1856
+ *
1857
+ * Cùng nếp với `ol-input`, `ol-checkbox`, `ol-select` — chúng đều
1858
+ * `computed(() => this.id() || this.uid)`.
1859
+ */
1860
+ private readonly uid;
1861
+ readonly inputIdInput: _angular_core.InputSignal<string>;
1862
+ readonly inputId: _angular_core.Signal<string>;
1863
+ /**
1864
+ * Tên trợ năng khi thẻ KHÔNG có nhãn chữ.
1865
+ *
1866
+ * Ca `control="none"` kèm `icon`: thẻ chỉ là một ô vuông có icon, `label()`
1867
+ * rỗng nên `<ol-label>` không render. Đo `/checkbox-radio`: ô nhập không
1868
+ * tên trong cây trợ năng dù bấm được (BUG-031).
1869
+ */
1870
+ readonly ariaLabel: _angular_core.InputSignal<string>;
1569
1871
  readonly changed: _angular_core.OutputEmitterRef<boolean>;
1570
1872
  /** `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
1873
  readonly interactive: _angular_core.Signal<boolean>;
@@ -1588,14 +1890,14 @@ declare class OlCheckboxRadioForm implements ControlValueAccessor {
1588
1890
  private onChange;
1589
1891
  private onTouched;
1590
1892
  /** 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;
1893
+ private initialized;
1592
1894
  constructor();
1593
1895
  writeValue(val: boolean): void;
1594
1896
  registerOnChange(fn: (val: boolean) => void): void;
1595
1897
  registerOnTouched(fn: () => void): void;
1596
1898
  setDisabledState(isDisabled: boolean): void;
1597
1899
  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>;
1900
+ 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
1901
  }
1600
1902
 
1601
1903
  /** Xếp dọc, xếp ngang, hay lưới nhiều cột. */
@@ -1669,11 +1971,35 @@ declare class OlInput implements ControlValueAccessor {
1669
1971
  readonly showAsterisk: _angular_core.InputSignal<boolean>;
1670
1972
  readonly labelIcon: _angular_core.InputSignal<boolean>;
1671
1973
  readonly labelTooltip: _angular_core.InputSignal<string>;
1974
+ /**
1975
+ * Tên trợ năng khi ô nhập KHÔNG có nhãn nhìn thấy được.
1976
+ *
1977
+ * `<label for>` chỉ render khi `label()` có chữ (`@if (label())` ở đầu
1978
+ * template), nên bố cục đặt chữ BÊN NGOÀI component — bảng lọc, thanh công
1979
+ * cụ, trang cài đặt — để lại một `<input>` hoàn toàn không tên. Có `label()`
1980
+ * thì không cần truyền cái này; hai thứ cùng lúc thì `aria-label` thắng và
1981
+ * người dùng bàn phím nghe một đằng, nhìn một nẻo.
1982
+ */
1983
+ readonly ariaLabel: _angular_core.InputSignal<string>;
1672
1984
  readonly prefixText: _angular_core.InputSignal<string>;
1673
1985
  readonly suffixText: _angular_core.InputSignal<string>;
1674
1986
  readonly addOnIcon: _angular_core.InputSignal<boolean>;
1675
1987
  readonly leftIcon: _angular_core.InputSignal<boolean>;
1676
1988
  readonly rightIcon: _angular_core.InputSignal<boolean>;
1989
+ /**
1990
+ * Hiện nút xoá khi ô có chữ.
1991
+ *
1992
+ * Mặc định **TẮT**, khác `ol-select` và `ol-datepicker` (mặc định bật).
1993
+ * Lý do: hai cái kia chọn từ danh sách nên xoá là thao tác thường gặp, còn
1994
+ * ô nhập chữ thì người dùng vẫn chọn-hết-rồi-gõ-đè như mọi ô nhập khác.
1995
+ * Bật sẵn cho MỌI `ol-input` đang chạy là thêm một nút vào giao diện của
1996
+ * người khác mà họ không yêu cầu.
1997
+ */
1998
+ readonly clearable: _angular_core.InputSignal<boolean>;
1999
+ /** Tên trợ năng của nút xoá. Đổi được khi ô có nghĩa cụ thể. */
2000
+ readonly clearAriaLabel: _angular_core.InputSignal<string>;
2001
+ /** Phát khi người dùng bấm nút xoá — nơi dùng biết để tải lại danh sách. */
2002
+ readonly cleared: _angular_core.OutputEmitterRef<void>;
1677
2003
  readonly rightHelperText: _angular_core.InputSignal<string>;
1678
2004
  readonly id: _angular_core.InputSignal<string>;
1679
2005
  /** Id thực dùng: ưu tiên id do nơi gọi truyền, không thì dùng id tự sinh. */
@@ -1705,10 +2031,18 @@ declare class OlInput implements ControlValueAccessor {
1705
2031
  setDisabledState(isDisabled: boolean): void;
1706
2032
  /** Gọi từ template mỗi khi giá trị đổi, để form biết. */
1707
2033
  protected phatThayDoi(val: string): void;
2034
+ /**
2035
+ * Xoá nội dung ô.
2036
+ *
2037
+ * `stopPropagation` vì nút nằm TRONG thẻ bọc: thiếu nó thì cú bấm lan ra
2038
+ * thẻ bọc, và ở component có hành vi bấm-để-mở (select, datepicker) sẽ mở
2039
+ * panel ngay sau khi xoá.
2040
+ */
2041
+ protected clearValue(event: MouseEvent): void;
1708
2042
  /** Gọi khi rời ô nhập — form dùng để đánh dấu `touched`. */
1709
- protected danhDauCham(): void;
2043
+ protected markTouched(): void;
1710
2044
  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>;
2045
+ 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; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; "clearAriaLabel": { "alias": "clearAriaLabel"; "required": false; "isSignal": true; }; "rightHelperText": { "alias": "rightHelperText"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; "cleared": "cleared"; }, never, ["[addOnIcon], [leftIcon]", "[leftIcon]", "[rightIcon]", "[rightHelperAction]"], true, never>;
1712
2046
  }
1713
2047
 
1714
2048
  type TextareaState = 'default' | 'error' | 'success';
@@ -1722,6 +2056,12 @@ declare class OlTextarea implements ControlValueAccessor {
1722
2056
  readonly ariaInvalid: _angular_core.Signal<"true" | null>;
1723
2057
  readonly value: _angular_core.ModelSignal<string>;
1724
2058
  readonly label: _angular_core.InputSignal<string>;
2059
+ /**
2060
+ * Tên trợ năng khi control KHÔNG có nhãn nhìn thấy được. Cùng lý do với
2061
+ * `ol-input.ariaLabel`: `<label>` chỉ render khi `label()` có chữ, nên bố
2062
+ * cục đặt chữ BÊN NGOÀI component để lại một control hoàn toàn không tên.
2063
+ */
2064
+ readonly ariaLabel: _angular_core.InputSignal<string>;
1725
2065
  readonly placeholder: _angular_core.InputSignal<string>;
1726
2066
  readonly rows: _angular_core.InputSignal<number>;
1727
2067
  /**
@@ -1754,16 +2094,60 @@ declare class OlTextarea implements ControlValueAccessor {
1754
2094
  /** Gọi từ template mỗi khi giá trị đổi, để form biết. */
1755
2095
  protected phatThayDoi(val: string): void;
1756
2096
  /** Gọi khi rời ô nhập — form dùng để đánh dấu `touched`. */
1757
- protected danhDauCham(): void;
2097
+ protected markTouched(): void;
1758
2098
  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>;
2099
+ 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
2100
  }
1761
2101
 
1762
2102
  /**
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.
2103
+ * Bảng chuẩn kích thước và trạng thái cho MỌI ô nhập của thư viện.
2104
+ *
2105
+ * ## Vì sao cần
2106
+ *
2107
+ * Chín component họ form từng tự khai kích thước riêng, và chúng trôi khỏi
2108
+ * nhau. Đo ngày 2026-09-23 trên chín component:
2109
+ *
2110
+ * thiếu cỡ `xl` 3 component (select · autocomplete · input-counter)
2111
+ * nền sai 3 component (autocomplete · timepicker · datepicker)
2112
+ * viền sai 5 component
2113
+ * đệm ngang lệch 2px ở cả ba cỡ đầu (datepicker so với input)
2114
+ * cỡ chữ `sm` lệch 2px (14px vs 12px)
2115
+ *
2116
+ * Nền và viền lệch **chỉ lộ ở chế độ tối**: sáng thì `--bg-secondary` và
2117
+ * `--bg-secondary-medium` cùng ra `#f9fafb`, tối thì `gray-950` vs `gray-800`.
2118
+ * Nên lỗi sống rất lâu — người dựng nhìn chế độ sáng, thấy giống hệt nhau.
2119
+ *
2120
+ * Đọc từ đây thì thêm ô nhập mới cũng tự khớp, và đổi thang chỉ sửa một chỗ.
2121
+ *
2122
+ * ## Không gộp chiều cao với đệm ngang
2123
+ *
2124
+ * Hai thứ tách riêng vì component có icon cần `ps-*` thay `px-*`. Gộp lại thì
2125
+ * mỗi component phải cắt chuỗi ra để bỏ phần đệm — dễ sai hơn là ghép vào.
2126
+ */
2127
+ /** Bốn cỡ của họ form. Trùng `InputSize`, `SelectSize`, `DatepickerSize`. */
2128
+ type FieldSize = 'sm' | 'base' | 'lg' | 'xl';
2129
+
2130
+ /**
2131
+ * Thang cỡ theo Figma: `sm | base | lg`.
2132
+ *
2133
+ * `'md'` là **BÍ DANH `@deprecated` của `'base'`** — quét toàn repo: **0 chỗ
2134
+ * dùng** (cả docs app lẫn lib). Giữ cho mã ngoài đang gọi vì `@edmicro/olkit`
2135
+ * đã publish công khai; bỏ input công khai là breaking change cho consumer mà
2136
+ * repo không đo được. Dùng `'base'` cho mã mới.
2137
+ *
2138
+ * Cùng cách xử lý với `variant`/`currentPage` của `ol-pagination`.
2139
+ *
2140
+ * Họ form là `sm | base | lg | xl` (`ol-input`, `ol-timepicker`,
2141
+ * `ol-datepicker`), và `ol-select` **có đủ cả bốn** — `xl` đi qua bảng chuẩn
2142
+ * `FIELD_HEIGHT`/`FIELD_PADDING_X` trong `forms/shared/field-tokens.ts` như
2143
+ * mọi cỡ khác.
2144
+ *
2145
+ * (Chú thích cũ ở đây ghi "ol-select thiếu xl" — đã LẠC HẬU, gỡ 2026-09-23.
2146
+ * Bảng API của ba trang docs chép theo nó nên cũng thiếu `xl`; `check:api:days`
2147
+ * bắt được, nhưng guard đó chưa bao giờ chạy vì vòng lặp gọi guard dùng regex
2148
+ * `check:[a-z-]*` — không khớp tên có HAI dấu hai chấm.)
1765
2149
  */
1766
- type SelectSize = 'sm' | 'base' | 'lg' | 'md';
2150
+ type SelectSize = 'sm' | 'base' | 'lg' | 'xl' | 'md';
1767
2151
  type SelectState = 'default' | 'error' | 'success';
1768
2152
  interface SelectOption {
1769
2153
  label: string;
@@ -1799,14 +2183,14 @@ interface SelectOption {
1799
2183
  [key: string]: any;
1800
2184
  }
1801
2185
  /** 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;
2186
+ interface FlatRow {
2187
+ kind: 'group' | 'option';
2188
+ label: string;
1805
2189
  item?: any;
1806
2190
  /** 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;
2191
+ index: number;
2192
+ /** Chỉ ở dòng `group`: đây có phải nhóm ĐẦU TIÊN không (để không kẻ đường ngăn thừa). */
2193
+ isFirstGroup?: boolean;
1810
2194
  }
1811
2195
  /**
1812
2196
  * `ol-select` — ô chọn có TÌM KIẾM, ĐA CHỌN, GOM NHÓM và CUỘN ẢO.
@@ -1835,6 +2219,11 @@ declare class OlSelect implements ControlValueAccessor {
1835
2219
  readonly value: _angular_core.ModelSignal<any>;
1836
2220
  readonly options: _angular_core.InputSignal<SelectOption[]>;
1837
2221
  readonly label: _angular_core.InputSignal<string>;
2222
+ /**
2223
+ * Tên trợ năng khi KHÔNG có `label()` nhìn thấy được. Có `label()` thì
2224
+ * `aria-labelledby` đã lo — không cần truyền cái này.
2225
+ */
2226
+ readonly ariaLabel: _angular_core.InputSignal<string>;
1838
2227
  readonly size: _angular_core.InputSignal<SelectSize>;
1839
2228
  readonly helperText: _angular_core.InputSignal<string>;
1840
2229
  readonly state: _angular_core.InputSignal<SelectState>;
@@ -1861,16 +2250,40 @@ declare class OlSelect implements ControlValueAccessor {
1861
2250
  /**
1862
2251
  * Đẩy danh sách ra `<body>` thay vì để trong cây DOM của component.
1863
2252
  *
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.
2253
+ * **Mặc định `'body'`** (từ bản này). Trước đây mặc định `null` — panel nằm
2254
+ * trong cây DOM của component, nên hễ đặt vào modal / drawer / bảng có
2255
+ * `overflow: hidden` là bị cắt, và người dùng phải tự biết mà bật. Đo
2256
+ * /modal: khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING
2257
+ * CONTEXT — z-index của lớp nổi không thoát ra được — và `overflow: hidden`
2258
+ * 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
2259
+ * cái thứ hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
1869
2260
  *
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.
2261
+ * Đặt `null` để quay về hành vi cũ: cần khi CSS của nơi dùng nhắm vào panel
2262
+ * qua cây DOM (`.form-cua-toi ol-select .panel`) — selector kiểu đó chết khi
2263
+ * panel treo ra ngoài. Cùng tên và cùng ý nghĩa với `appendTo` của ng-select
2264
+ * và PrimeNG.
1872
2265
  */
1873
2266
  readonly appendTo: _angular_core.InputSignal<"body" | null>;
2267
+ /**
2268
+ * Lớp Tailwind cho panel thả xuống.
2269
+ *
2270
+ * Cùng tên và cùng ý nghĩa với `menuClass` của `ol-dropdown`.
2271
+ *
2272
+ * Chỉ có tác dụng khi `matchTriggerWidth` TẮT: bề rộng panel được đặt bằng
2273
+ * `style.width` inline, mà style inline thắng mọi lớp Tailwind.
2274
+ */
2275
+ readonly menuClass: _angular_core.InputSignal<string>;
2276
+ /**
2277
+ * Panel rộng đúng bằng ô neo hay không. Mặc định **bật** — giữ hành vi cũ.
2278
+ *
2279
+ * Tắt khi nội dung cần rộng hơn ô: nhãn dài, hai cột, hoặc nội dung tuỳ
2280
+ * biến truyền qua `ng-template`. Lúc đó `menuClass` mới có chỗ tác dụng.
2281
+ *
2282
+ * ```html
2283
+ * <ol-select [matchTriggerWidth]="false" menuClass="w-96" … />
2284
+ * ```
2285
+ */
2286
+ readonly matchTriggerWidth: _angular_core.InputSignal<boolean>;
1874
2287
  /** Tối đa bao nhiêu mục được chọn ở chế độ đa chọn. 0 = không giới hạn. */
1875
2288
  readonly maxSelectedItems: _angular_core.InputSignal<number>;
1876
2289
  /** Đóng danh sách ngay sau khi chọn. Mặc định: đơn chọn đóng, đa chọn không. */
@@ -1970,7 +2383,7 @@ declare class OlSelect implements ControlValueAccessor {
1970
2383
  */
1971
2384
  private readonly panel;
1972
2385
  /** Vùng CUỘN — là thẻ `role="listbox"`, không phải hộp bọc. */
1973
- private readonly danhSach;
2386
+ private readonly listboxRef;
1974
2387
  /** Ô tìm ở đầu panel, để chuyển focus vào khi mở. */
1975
2388
  private readonly oTimPanel;
1976
2389
  readonly isOpen: _angular_core.WritableSignal<boolean>;
@@ -1981,26 +2394,61 @@ declare class OlSelect implements ControlValueAccessor {
1981
2394
  private readonly uid;
1982
2395
  readonly inputId: _angular_core.Signal<string>;
1983
2396
  readonly helperId: _angular_core.Signal<string>;
2397
+ /**
2398
+ * Id của `<label>`, để combobox trỏ vào bằng `aria-labelledby`.
2399
+ *
2400
+ * `<label for>` chỉ đặt tên cho form control GỐC (`input`, `select`,
2401
+ * `textarea`, `button`) — KHÔNG cho `<div role="combobox">` dù id khớp
2402
+ * hoàn toàn. Đo `/select`: 32/32 combobox không có tên trong cây trợ năng
2403
+ * trong khi `labelFor` và `idCombobox` khớp y nhau (BUG-030).
2404
+ */
2405
+ readonly labelId: _angular_core.Signal<string>;
2406
+ /**
2407
+ * Id của nhãn cố định NẰM TRONG ô đóng (`triggerLabel`).
2408
+ *
2409
+ * Chữ đó đã có trong `textContent` của combobox, nhưng `role="combobox"`
2410
+ * khiến trình đọc màn hình coi nội dung là GIÁ TRỊ chứ không phải TÊN —
2411
+ * nghe ra "combobox, Khối: Khối 10" mà vẫn không biết đang chọn cái gì.
2412
+ * Trỏ vào chính `<span>` đó nên tên nghe được trùng khít chữ nhìn thấy.
2413
+ */
2414
+ readonly triggerLabelId: _angular_core.Signal<string>;
1984
2415
  readonly listboxId: _angular_core.Signal<string>;
1985
2416
  readonly describedBy: _angular_core.Signal<string | null>;
1986
2417
  readonly ariaInvalid: _angular_core.Signal<"true" | null>;
1987
- protected readonly resolvedSize: _angular_core.Signal<SelectSize>;
2418
+ /**
2419
+ * Cỡ đã bỏ bí danh `'md'`.
2420
+ *
2421
+ * Khai kiểu trả về là `FieldSize` chứ không để TypeScript suy: suy ra sẽ
2422
+ * giữ `'md'` trong kiểu hợp dù giá trị không bao giờ là `'md'`, và mọi chỗ
2423
+ * tra bảng chuẩn sẽ báo lỗi "Property 'md' does not exist".
2424
+ */
2425
+ protected readonly resolvedSize: _angular_core.Signal<FieldSize>;
1988
2426
  /**
1989
2427
  * Padding trái: 10 khi có icon, 3.5 khi không.
1990
2428
  * Không dùng `[class.ps-3.5]` vì Angular cắt phần sau dấu chấm — nó render
1991
2429
  * ra `ps-3` (12px thay vì 14px).
1992
2430
  */
1993
- readonly paddingStartClass: _angular_core.Signal<"ps-10" | "ps-3.5">;
2431
+ /**
2432
+ * Đệm trái theo bảng chuẩn, KHÔNG khoá cứng một giá trị cho mọi cỡ.
2433
+ *
2434
+ * Bản cũ trả `ps-3.5` (14px) cho cả bốn cỡ, trong khi `ol-input` dùng
2435
+ * 10/12/14/16px. Đặt `ol-select size="sm"` cạnh `ol-input size="sm"` là
2436
+ * lệch 4px, và ở `xl` lệch 2px theo chiều ngược lại.
2437
+ *
2438
+ * Có icon trái thì `ps-10` cố định — chỗ đó dành cho icon, không phải đệm.
2439
+ */
2440
+ readonly paddingStartClass: _angular_core.Signal<string>;
1994
2441
  private onChange;
1995
2442
  private onTouched;
1996
2443
  private readonly destroyRef;
1997
2444
  private readonly elementRef;
1998
2445
  private readonly overlayContainer;
1999
- private readonly positioning;
2000
2446
  /** 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;
2447
+ private detachPanel;
2448
+ /** Hàm gỡ khỏi ngăn xếp Escape. `null` khi panel đang đóng. */
2449
+ private releaseEscape;
2450
+ private stopTracking;
2451
+ private wasOpen;
2004
2452
  private hengio;
2005
2453
  constructor();
2006
2454
  /** Ngắt theo dõi vị trí và trả panel về chỗ cũ. Gọi được nhiều lần. */
@@ -2010,7 +2458,7 @@ declare class OlSelect implements ControlValueAccessor {
2010
2458
  * trị mặc định, và `x < undefined` ra `NaN` — luôn `false`. Cùng lớp lỗi
2011
2459
  * BUG-009, nên mọi input số đều đi qua một hàm lọc.
2012
2460
  */
2013
- private soDuong;
2461
+ private positiveOr;
2014
2462
  private readonly safeMinTerm;
2015
2463
  private readonly safeDebounce;
2016
2464
  private readonly safeItemHeight;
@@ -2018,7 +2466,7 @@ declare class OlSelect implements ControlValueAccessor {
2018
2466
  readonly safeMaxSelected: _angular_core.Signal<number>;
2019
2467
  getItemLabel(item: any): string;
2020
2468
  getItemValue(item: any): any;
2021
- private bangNhau;
2469
+ private sameValue;
2022
2470
  /**
2023
2471
  * Giá trị đang chọn, LUÔN trả mảng — template chỉ có một đường đọc.
2024
2472
  *
@@ -2050,15 +2498,30 @@ declare class OlSelect implements ControlValueAccessor {
2050
2498
  */
2051
2499
  readonly selectedItem: _angular_core.Signal<SelectOption | null>;
2052
2500
  /** 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;
2501
+ optionOf(val: any): SelectOption | null;
2054
2502
  /** Mục này có phần đầu (icon hoặc chữ) để vẽ không. */
2055
- coPhanDau(item: SelectOption | null | undefined): boolean;
2503
+ hasPrefix(item: SelectOption | null | undefined): boolean;
2056
2504
  /**
2057
2505
  * 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
2506
  * `hasValue()`. Khi mọi mục đã chọn đều `locked`, nút hiện mà bấm không làm
2059
2507
  * gì: nút chết còn khó hiểu hơn là không có nút.
2060
2508
  */
2061
- readonly coTheXoa: _angular_core.Signal<boolean>;
2509
+ readonly hasClearable: _angular_core.Signal<boolean>;
2510
+ /**
2511
+ * Có gì để nút xoá dọn hay không — mục đã chọn HOẶC chữ đang gõ ở ô tìm.
2512
+ *
2513
+ * `hasClearable()` chỉ nhìn mục đã chọn, nên gõ vào ô tìm mà chưa chọn gì
2514
+ * thì không có nút — người dùng gõ sai phải xoá từng ký tự. Đo /select: gõ
2515
+ * "to" vào ô tìm, số nút xoá vẫn 0.
2516
+ *
2517
+ * Nút dọn cả hai thứ (`clear()` đã gọi `searchQuery.set('')`), nên điều
2518
+ * kiện hiện phải nhìn cả hai.
2519
+ *
2520
+ * Tên `hasContent` dùng CHUNG với `ol-datepicker` và `ol-date-range-picker`
2521
+ * — cùng một khái niệm thì cùng một tên, để người đọc mã không phải nhớ ba
2522
+ * cách gọi cho một thứ.
2523
+ */
2524
+ readonly hasContent: _angular_core.Signal<boolean>;
2062
2525
  /**
2063
2526
  * Có hiện lối tạo mục mới không.
2064
2527
  *
@@ -2066,7 +2529,7 @@ declare class OlSelect implements ControlValueAccessor {
2066
2529
  * 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
2530
  * mới" cùng tên chỉ làm họ tạo ra bản trùng.
2068
2531
  */
2069
- readonly coTheTao: _angular_core.Signal<boolean>;
2532
+ readonly canCreate: _angular_core.Signal<boolean>;
2070
2533
  /** Chữ trên nút tạo mới, đã thay `{term}`. */
2071
2534
  readonly createLabel: _angular_core.Signal<string>;
2072
2535
  /**
@@ -2076,7 +2539,7 @@ declare class OlSelect implements ControlValueAccessor {
2076
2539
  * 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
2540
  * nghe `added` rồi tự quyết có đưa vào danh sách hay không.
2078
2541
  */
2079
- taoMuc(): void;
2542
+ createOption(): void;
2080
2543
  readonly waitingForTerm: _angular_core.Signal<boolean>;
2081
2544
  readonly filteredOptions: _angular_core.Signal<SelectOption[]>;
2082
2545
  /**
@@ -2086,17 +2549,26 @@ declare class OlSelect implements ControlValueAccessor {
2086
2549
  * theo chỉ số phẳng; lồng vào thì `$index` reset mỗi nhóm và điều hướng bàn
2087
2550
  * phím sai hết. Cùng lý do đã ghi ở `ol-autocomplete`.
2088
2551
  */
2089
- readonly rows: _angular_core.Signal<Dong[]>;
2552
+ readonly rows: _angular_core.Signal<FlatRow[]>;
2090
2553
  /** Chỉ các dòng CHỌN ĐƯỢC — bàn phím chạy trên danh sách này. */
2091
2554
  readonly selectableItems: _angular_core.Signal<SelectOption[]>;
2092
2555
  /**
2093
2556
  * 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.
2557
+ * panel. CHƯA CHỌN GÌ thì trả `-1`: không mục nào được đánh dấu.
2558
+ *
2559
+ * Bản trước trả `0` cho ca chưa chọn, nên mở dropdown ra là option đầu đã có
2560
+ * nền xám đặc trong khi `aria-selected="false"` — người dùng NHÌN thấy "đã
2561
+ * 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
2562
+ * ngay lúc đó là **chọn nhầm option đầu**. Đo /select: 5/5 ô chưa chọn đều
2563
+ * dính (BUG-021).
2564
+ *
2565
+ * Đây đúng là hậu quả mà chú thích của `open()` từng cảnh báo cho ca ĐÃ có giá
2566
+ * trị — bản sửa đó chữa nửa trên mà giữ nguyên nửa dưới.
2095
2567
  *
2096
2568
  * Đ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
2569
  * đã 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
2570
  */
2099
- private chiSoMucDangChon;
2571
+ private selectedOptionIndex;
2100
2572
  /**
2101
2573
  * Khoảng dòng thật sự được vẽ. Nguyên lý lấy từ `ng-select`:
2102
2574
  * `scrollTop / itemHeight` cho mục đầu trong tầm nhìn, cộng thêm số dòng
@@ -2106,14 +2578,23 @@ declare class OlSelect implements ControlValueAccessor {
2106
2578
  * trong template.
2107
2579
  */
2108
2580
  readonly viewport: _angular_core.Signal<{
2109
- rows: Dong[];
2110
- dauPad: number;
2111
- cuoiPad: number;
2581
+ rows: FlatRow[];
2582
+ padTop: number;
2583
+ padBottom: number;
2112
2584
  }>;
2113
2585
  onPanelScroll(e: Event): void;
2114
2586
  onDocumentClick(e: MouseEvent): void;
2115
2587
  toggle(): void;
2588
+ open(): void;
2589
+ close(): void;
2590
+ /**
2591
+ * @deprecated Dùng `open()`. Giữ lại vì `@edmicro/olkit` đã publish lên npm
2592
+ * công khai — bỏ phương thức công khai là breaking change cho consumer mà
2593
+ * repo này không đo được. Theo đúng nếp `@deprecated` ở `variant` của
2594
+ * `ol-pagination` và `onPageSizeChangeEvent`.
2595
+ */
2116
2596
  mo(): void;
2597
+ /** @deprecated Dùng `close()`. Cùng lý do với `mo()`. */
2117
2598
  dong(): void;
2118
2599
  onSearchInput(val: string): void;
2119
2600
  /**
@@ -2122,7 +2603,7 @@ declare class OlSelect implements ControlValueAccessor {
2122
2603
  */
2123
2604
  private phatSearch;
2124
2605
  /** Đơ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;
2606
+ private shouldClose;
2126
2607
  selectItem(item: any): void;
2127
2608
  /**
2128
2609
  * Mục này có bị khoá không gỡ không.
@@ -2134,16 +2615,16 @@ declare class OlSelect implements ControlValueAccessor {
2134
2615
  isLocked(val: any): boolean;
2135
2616
  removeValue(val: any, e: Event): void;
2136
2617
  clear(e: Event): void;
2137
- private datGiaTri;
2618
+ private setValue;
2138
2619
  onKeyDown(e: KeyboardEvent): void;
2139
- private diChuyen;
2620
+ private moveActive;
2140
2621
  /**
2141
2622
  * Kéo mục đang đánh dấu vào tầm nhìn.
2142
2623
  *
2143
2624
  * 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.
2625
+ * Tính thẳng `index * itemHeight` mới đúng.
2145
2626
  */
2146
- private cuonToiMuc;
2627
+ private scrollToOption;
2147
2628
  optionId(i: number): string;
2148
2629
  readonly activeOptionId: _angular_core.Signal<string | null>;
2149
2630
  writeValue(val: any): void;
@@ -2151,7 +2632,7 @@ declare class OlSelect implements ControlValueAccessor {
2151
2632
  registerOnTouched(fn: () => void): void;
2152
2633
  setDisabledState(isDisabled: boolean): void;
2153
2634
  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>;
2635
+ 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; }; "menuClass": { "alias": "menuClass"; "required": false; "isSignal": true; }; "matchTriggerWidth": { "alias": "matchTriggerWidth"; "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
2636
  }
2156
2637
 
2157
2638
  /**
@@ -2162,6 +2643,12 @@ type CheckboxSize = 'sm' | 'base' | 'lg' | 'md';
2162
2643
  declare class OlCheckbox implements ControlValueAccessor {
2163
2644
  readonly checked: _angular_core.ModelSignal<boolean>;
2164
2645
  readonly label: _angular_core.InputSignal<string>;
2646
+ /**
2647
+ * Tên trợ năng khi control KHÔNG có nhãn nhìn thấy được. Cùng lý do với
2648
+ * `ol-input.ariaLabel`: `<label>` chỉ render khi `label()` có chữ, nên bố
2649
+ * cục đặt chữ BÊN NGOÀI component để lại một control hoàn toàn không tên.
2650
+ */
2651
+ readonly ariaLabel: _angular_core.InputSignal<string>;
2165
2652
  readonly description: _angular_core.InputSignal<string>;
2166
2653
  /**
2167
2654
  * Khai bằng `model()` thay vì `input()` để Reactive Forms khoá được component:
@@ -2200,7 +2687,7 @@ declare class OlCheckbox implements ControlValueAccessor {
2200
2687
  registerOnTouched(fn: any): void;
2201
2688
  setDisabledState(isDisabled: boolean): void;
2202
2689
  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>;
2690
+ 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
2691
  }
2205
2692
 
2206
2693
  /**
@@ -2214,6 +2701,12 @@ declare class OlRadio implements ControlValueAccessor {
2214
2701
  readonly selectedValue: _angular_core.ModelSignal<any>;
2215
2702
  readonly modelValue: _angular_core.ModelSignal<any>;
2216
2703
  readonly label: _angular_core.InputSignal<string>;
2704
+ /**
2705
+ * Tên trợ năng khi control KHÔNG có nhãn nhìn thấy được. Cùng lý do với
2706
+ * `ol-input.ariaLabel`: `<label>` chỉ render khi `label()` có chữ, nên bố
2707
+ * cục đặt chữ BÊN NGOÀI component để lại một control hoàn toàn không tên.
2708
+ */
2709
+ readonly ariaLabel: _angular_core.InputSignal<string>;
2217
2710
  readonly description: _angular_core.InputSignal<string>;
2218
2711
  /**
2219
2712
  * Khai bằng `model()` thay vì `input()` để Reactive Forms khoá được component:
@@ -2238,7 +2731,7 @@ declare class OlRadio implements ControlValueAccessor {
2238
2731
  registerOnTouched(fn: any): void;
2239
2732
  setDisabledState(isDisabled: boolean): void;
2240
2733
  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>;
2734
+ 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
2735
  }
2243
2736
 
2244
2737
  /**
@@ -2250,6 +2743,12 @@ type ToggleColor = 'brand' | 'orange' | 'green' | 'red';
2250
2743
  declare class OlToggle implements ControlValueAccessor {
2251
2744
  readonly checked: _angular_core.ModelSignal<boolean>;
2252
2745
  readonly label: _angular_core.InputSignal<string>;
2746
+ /**
2747
+ * Tên trợ năng khi control KHÔNG có nhãn nhìn thấy được. Cùng lý do với
2748
+ * `ol-input.ariaLabel`: `<label>` chỉ render khi `label()` có chữ, nên bố
2749
+ * cục đặt chữ BÊN NGOÀI component để lại một control hoàn toàn không tên.
2750
+ */
2751
+ readonly ariaLabel: _angular_core.InputSignal<string>;
2253
2752
  readonly description: _angular_core.InputSignal<string>;
2254
2753
  /**
2255
2754
  * Khai bằng `model()` thay vì `input()` để Reactive Forms khoá được component:
@@ -2271,7 +2770,7 @@ declare class OlToggle implements ControlValueAccessor {
2271
2770
  registerOnTouched(fn: any): void;
2272
2771
  setDisabledState(isDisabled: boolean): void;
2273
2772
  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>;
2773
+ 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
2774
  }
2276
2775
 
2277
2776
  /** Công tắc đứng bên nào của hàng. Figma `Type=Default` để bên trái. */
@@ -2322,7 +2821,7 @@ declare class OlToggleInput implements ControlValueAccessor {
2322
2821
  private onChange;
2323
2822
  private onTouched;
2324
2823
  /** 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;
2824
+ private initialized;
2326
2825
  constructor();
2327
2826
  writeValue(val: boolean): void;
2328
2827
  registerOnChange(fn: (val: boolean) => void): void;
@@ -2416,6 +2915,29 @@ declare class OlRange implements ControlValueAccessor {
2416
2915
  private readonly generatedId;
2417
2916
  protected readonly inputId: _angular_core.Signal<string>;
2418
2917
  readonly helperId: _angular_core.Signal<string>;
2918
+ /**
2919
+ * Id riêng cho HAI tay trượt ở chế độ `dualRange`.
2920
+ *
2921
+ * Nhánh `dualRange` trước đây không có `[id]`, không `aria-label`, không
2922
+ * `aria-describedby` — `<label for>` ở trên trỏ vào `inputId()` mà không
2923
+ * input nào mang id đó. Đo `/range-slider`: **4/8 tay trượt không có tên**
2924
+ * trong cây trợ năng (BUG-025).
2925
+ *
2926
+ * Người dùng bàn phím nghe "slider" trống trơn, không biết mình đang kéo
2927
+ * đầu nào của khoảng.
2928
+ */
2929
+ readonly minThumbId: _angular_core.Signal<string>;
2930
+ readonly maxThumbId: _angular_core.Signal<string>;
2931
+ /**
2932
+ * Tên của từng tay trượt: ghép nhãn chung với vai trò.
2933
+ *
2934
+ * Có `label()` thì ra "Khoảng giá — giá trị nhỏ nhất"; không có thì lùi về
2935
+ * `ariaLabel()` của nơi gọi, rồi mới tới tên trần. KHÔNG để trống: ở chế độ
2936
+ * dual, `<label for>` chỉ nối được MỘT trong hai tay trượt.
2937
+ */
2938
+ readonly ariaLabel: _angular_core.InputSignal<string>;
2939
+ readonly minThumbLabel: _angular_core.Signal<string>;
2940
+ readonly maxThumbLabel: _angular_core.Signal<string>;
2419
2941
  readonly describedBy: _angular_core.Signal<string | null>;
2420
2942
  readonly ariaInvalid: _angular_core.Signal<null>;
2421
2943
  readonly singlePercentage: _angular_core.Signal<number>;
@@ -2431,7 +2953,7 @@ declare class OlRange implements ControlValueAccessor {
2431
2953
  registerOnTouched(fn: any): void;
2432
2954
  setDisabledState(isDisabled: boolean): void;
2433
2955
  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>;
2956
+ 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
2957
  }
2436
2958
 
2437
2959
  /**
@@ -2498,7 +3020,7 @@ declare class OlFloatingLabel implements ControlValueAccessor {
2498
3020
  * Thang cỡ theo Figma: `sm | base | lg`. `'md'` là BÍ DANH của `'base'`,
2499
3021
  * giữ cho mã cũ chạy tiếp và sẽ bỏ ở bản sau — dùng `'base'` cho mã mới.
2500
3022
  */
2501
- type InputCounterSize = 'sm' | 'base' | 'lg' | 'md';
3023
+ type InputCounterSize = 'sm' | 'base' | 'lg' | 'xl' | 'md';
2502
3024
  declare class OlInputCounter implements ControlValueAccessor {
2503
3025
  /** Id tự sinh khi nơi gọi không truyền id — để <label for> và
2504
3026
  * aria-describedby luôn nối được với ô nhập. */
@@ -2544,7 +3066,7 @@ declare class OlInputCounter implements ControlValueAccessor {
2544
3066
  /** Nhãn: `inline` bỏ margin dưới vì nó nằm cạnh, không nằm trên. */
2545
3067
  protected readonly labelClasses: _angular_core.Signal<"shrink-0 text-sm font-medium text-heading" | "block mb-1.5 text-sm font-medium text-heading">;
2546
3068
  /** Khối nút+ô: `ghost` bỏ nền, viền và đổ bóng. */
2547
- protected readonly groupClasses: _angular_core.Signal<"relative inline-flex items-center overflow-hidden" | "relative inline-flex items-center rounded-md shadow-xs border border-default bg-neutral-primary overflow-hidden">;
3069
+ protected readonly groupClasses: _angular_core.Signal<"relative inline-flex items-center overflow-hidden" | "relative inline-flex items-center rounded-md shadow-xs border border-default-medium bg-neutral-primary overflow-hidden">;
2548
3070
  /** Nút trong chế độ `ghost` cũng phải bỏ nền và đường ngăn. */
2549
3071
  protected readonly ghostNut: _angular_core.Signal<"" | "bg-transparent border-0">;
2550
3072
  increment(): void;
@@ -2557,7 +3079,7 @@ declare class OlInputCounter implements ControlValueAccessor {
2557
3079
  private onChange;
2558
3080
  private onTouched;
2559
3081
  /** 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;
3082
+ private initialized;
2561
3083
  constructor();
2562
3084
  writeValue(val: number): void;
2563
3085
  registerOnChange(fn: (val: number) => void): void;
@@ -2626,7 +3148,7 @@ declare class OlOtpInput implements ControlValueAccessor {
2626
3148
  private onChange;
2627
3149
  private onTouched;
2628
3150
  /** 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;
3151
+ private initialized;
2630
3152
  constructor();
2631
3153
  writeValue(val: string): void;
2632
3154
  registerOnChange(fn: (val: string) => void): void;
@@ -2685,14 +3207,20 @@ declare class OlTagInput implements ControlValueAccessor {
2685
3207
  private onChange;
2686
3208
  private onTouched;
2687
3209
  /** Lần chạy đầu của `effect` không phải người dùng đổi — bỏ qua. */
2688
- private daKhoiTao;
3210
+ private initialized;
2689
3211
  constructor();
2690
3212
  writeValue(val: string[]): void;
2691
3213
  registerOnChange(fn: (val: string[]) => void): void;
2692
3214
  registerOnTouched(fn: () => void): void;
2693
3215
  setDisabledState(isDisabled: boolean): void;
2694
- /** Gọi khi rời ô nhập — form dùng để đánh dấu `touched`. */
2695
- protected danhDauCham(): void;
3216
+ /**
3217
+ * Gọi khi rời ô nhập — form dùng để đánh dấu `touched`.
3218
+ *
3219
+ * Trước đây hàm này KHÔNG được gọi ở đâu cả (template không có `(blur)`),
3220
+ * nên `onTouched()` chưa bao giờ chạy: Reactive Forms không đánh dấu control
3221
+ * là `touched`, và mọi kiểm tra dựa vào `ng-touched` im lặng không hoạt động.
3222
+ */
3223
+ protected markTouched(): void;
2696
3224
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlTagInput, never>;
2697
3225
  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
3226
  }
@@ -2733,7 +3261,7 @@ declare class OlRichTextEditor implements ControlValueAccessor {
2733
3261
  private onChange;
2734
3262
  private onTouched;
2735
3263
  /** 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;
3264
+ private initialized;
2737
3265
  constructor();
2738
3266
  writeValue(val: string): void;
2739
3267
  registerOnChange(fn: (val: string) => void): void;
@@ -2762,6 +3290,11 @@ declare class OlTimepicker implements ControlValueAccessor {
2762
3290
  private readonly elementRef;
2763
3291
  readonly value: _angular_core.ModelSignal<string>;
2764
3292
  readonly label: _angular_core.InputSignal<string>;
3293
+ /**
3294
+ * Tên trợ năng khi KHÔNG có `label()` nhìn thấy được. Có `label()` thì
3295
+ * `aria-labelledby` đã lo — không cần truyền cái này.
3296
+ */
3297
+ readonly ariaLabel: _angular_core.InputSignal<string>;
2765
3298
  readonly placeholder: _angular_core.InputSignal<string>;
2766
3299
  readonly format: _angular_core.InputSignal<TimepickerFormat>;
2767
3300
  readonly size: _angular_core.InputSignal<TimepickerSize>;
@@ -2782,14 +3315,18 @@ declare class OlTimepicker implements ControlValueAccessor {
2782
3315
  /**
2783
3316
  * Đẩy bảng chọn giờ ra `<body>` thay vì để trong cây DOM của component.
2784
3317
  *
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`.
3318
+ * **Mặc định `'body'`** (từ bản này). Trước đây mặc định `null` — panel nằm
3319
+ * trong cây DOM của component, nên hễ đặt vào modal / drawer / bảng có
3320
+ * `overflow: hidden` là bị cắt, và người dùng phải tự biết mà bật. Đo
3321
+ * /modal: khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING
3322
+ * CONTEXT — z-index của lớp nổi không thoát ra được — và `overflow: hidden`
3323
+ * 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
3324
+ * cái thứ hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
2790
3325
  *
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.
3326
+ * Đặt `null` để quay về hành vi cũ: cần khi CSS của nơi dùng nhắm vào panel
3327
+ * qua cây DOM (`.form-cua-toi ol-select .panel`) — selector kiểu đó chết khi
3328
+ * panel treo ra ngoài. Cùng tên và cùng ý nghĩa với `appendTo` của ng-select
3329
+ * và PrimeNG.
2793
3330
  */
2794
3331
  readonly appendTo: _angular_core.InputSignal<"body" | null>;
2795
3332
  readonly showPresets: _angular_core.InputSignal<boolean>;
@@ -2799,14 +3336,19 @@ declare class OlTimepicker implements ControlValueAccessor {
2799
3336
  private readonly generatedId;
2800
3337
  protected readonly inputId: _angular_core.Signal<string>;
2801
3338
  readonly helperId: _angular_core.Signal<string>;
3339
+ /**
3340
+ * Id của `<label>`, để combobox trỏ vào bằng `aria-labelledby`. Cùng lỗi
3341
+ * với `ol-select`: `<label for>` không đặt tên cho `<div role="combobox">`
3342
+ * dù id khớp — đo `/timepicker` ra 39 node không tên (BUG-031).
3343
+ */
3344
+ readonly labelId: _angular_core.Signal<string>;
2802
3345
  readonly describedBy: _angular_core.Signal<string | null>;
2803
3346
  readonly ariaInvalid: _angular_core.Signal<"true" | null>;
2804
3347
  private readonly panel;
2805
3348
  private readonly overlayContainer;
2806
- private readonly positioning;
2807
3349
  /** Hàm trả panel về chỗ cũ + hàm ngắt theo dõi vị trí. */
2808
- private goPanel;
2809
- private ngatTheoDoi;
3350
+ private detachPanel;
3351
+ private stopTracking;
2810
3352
  readonly isOpen: _angular_core.WritableSignal<boolean>;
2811
3353
  readonly selectedHour: _angular_core.WritableSignal<number>;
2812
3354
  readonly selectedMinute: _angular_core.WritableSignal<number>;
@@ -2850,14 +3392,14 @@ declare class OlTimepicker implements ControlValueAccessor {
2850
3392
  registerOnTouched(fn: any): void;
2851
3393
  setDisabledState(isDisabled: boolean): void;
2852
3394
  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>;
3395
+ 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
3396
  }
2855
3397
 
2856
3398
  /**
2857
3399
  * Thang cỡ theo Figma: `sm | base | lg`. `'md'` là BÍ DANH của `'base'`,
2858
3400
  * giữ cho mã cũ chạy tiếp và sẽ bỏ ở bản sau — dùng `'base'` cho mã mới.
2859
3401
  */
2860
- type AutocompleteSize = 'sm' | 'base' | 'lg' | 'md';
3402
+ type AutocompleteSize = 'sm' | 'base' | 'lg' | 'xl' | 'md';
2861
3403
  type AutocompleteState = 'default' | 'error' | 'success';
2862
3404
  interface AutocompleteOption {
2863
3405
  label: string;
@@ -2900,16 +3442,40 @@ declare class OlAutocomplete implements ControlValueAccessor {
2900
3442
  /**
2901
3443
  * Đẩy danh sách gợi ý ra `<body>` thay vì để trong cây DOM của component.
2902
3444
  *
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`.
3445
+ * **Mặc định `'body'`** (từ bản này). Trước đây mặc định `null` — panel nằm
3446
+ * trong cây DOM của component, nên hễ đặt vào modal / drawer / bảng có
3447
+ * `overflow: hidden` là bị cắt, và người dùng phải tự biết mà bật. Đo
3448
+ * /modal: khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING
3449
+ * CONTEXT — z-index của lớp nổi không thoát ra được — và `overflow: hidden`
3450
+ * 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
3451
+ * cái thứ hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
2908
3452
  *
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.
3453
+ * Đặt `null` để quay về hành vi cũ: cần khi CSS của nơi dùng nhắm vào panel
3454
+ * qua cây DOM (`.form-cua-toi ol-select .panel`) — selector kiểu đó chết khi
3455
+ * panel treo ra ngoài. Cùng tên và cùng ý nghĩa với `appendTo` của ng-select
3456
+ * và PrimeNG.
2911
3457
  */
2912
3458
  readonly appendTo: _angular_core.InputSignal<"body" | null>;
3459
+ /**
3460
+ * Lớp Tailwind cho panel thả xuống.
3461
+ *
3462
+ * Cùng tên và cùng ý nghĩa với `menuClass` của `ol-dropdown`.
3463
+ *
3464
+ * Chỉ có tác dụng khi `matchTriggerWidth` TẮT: bề rộng panel được đặt bằng
3465
+ * `style.width` inline, mà style inline thắng mọi lớp Tailwind.
3466
+ */
3467
+ readonly menuClass: _angular_core.InputSignal<string>;
3468
+ /**
3469
+ * Panel rộng đúng bằng ô neo hay không. Mặc định **bật** — giữ hành vi cũ.
3470
+ *
3471
+ * Tắt khi nội dung cần rộng hơn ô: nhãn dài, hai cột, hoặc nội dung tuỳ
3472
+ * biến truyền qua `ng-template`. Lúc đó `menuClass` mới có chỗ tác dụng.
3473
+ *
3474
+ * ```html
3475
+ * <ol-select [matchTriggerWidth]="false" menuClass="w-96" … />
3476
+ * ```
3477
+ */
3478
+ readonly matchTriggerWidth: _angular_core.InputSignal<boolean>;
2913
3479
  readonly groupBy: _angular_core.InputSignal<string>;
2914
3480
  /**
2915
3481
  * LỌC TỪ XA. Bật lên thì component NGỪNG tự lọc `options` — nó chỉ phát
@@ -2959,13 +3525,14 @@ declare class OlAutocomplete implements ControlValueAccessor {
2959
3525
  private onTouched;
2960
3526
  private readonly panel;
2961
3527
  private readonly overlayContainer;
2962
- private readonly positioning;
2963
3528
  /** Hàm trả panel về chỗ cũ + hàm ngắt theo dõi vị trí. */
2964
- private goPanel;
2965
- private ngatTheoDoi;
3529
+ private detachPanel;
3530
+ /** Hàm gỡ khỏi ngăn xếp Escape. `null` khi panel đang đóng. */
3531
+ private releaseEscape;
3532
+ private stopTracking;
2966
3533
  private readonly destroyRef;
2967
3534
  /** Trạng thái mở ở lượt chạy trước — để `effect` chỉ phát khi ĐỔI. */
2968
- private daMo;
3535
+ private wasOpen;
2969
3536
  /** Hẹn giờ gỡ nảy cho `search`. */
2970
3537
  private hengio;
2971
3538
  constructor(elementRef: ElementRef);
@@ -2998,7 +3565,7 @@ declare class OlAutocomplete implements ControlValueAccessor {
2998
3565
  * một đường render thay vì hai nhánh song song.
2999
3566
  */
3000
3567
  readonly groupedOptions: _angular_core.Signal<{
3001
- ten: string;
3568
+ name: string;
3002
3569
  items: any[];
3003
3570
  }[]>;
3004
3571
  /**
@@ -3008,7 +3575,7 @@ declare class OlAutocomplete implements ControlValueAccessor {
3008
3575
  * nhóm: `activeIndex()` và `optionId(idx)` đều đánh theo chỉ số phẳng, lồng
3009
3576
  * vào thì `$index` reset mỗi nhóm và điều hướng bàn phím sai hết.
3010
3577
  */
3011
- tenNhomTaiVitri(idx: number): string | null;
3578
+ groupNameAt(idx: number): string | null;
3012
3579
  getItemLabel(item: any): string;
3013
3580
  getItemValue(item: any): any;
3014
3581
  isSelected(item: any): boolean;
@@ -3037,7 +3604,7 @@ declare class OlAutocomplete implements ControlValueAccessor {
3037
3604
  registerOnTouched(fn: any): void;
3038
3605
  setDisabledState(isDisabled: boolean): void;
3039
3606
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlAutocomplete, never>;
3040
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlAutocomplete, "ol-autocomplete", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "valueKey": { "alias": "valueKey"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "showAsterisk": { "alias": "showAsterisk"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "errorText": { "alias": "errorText"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "allowCreate": { "alias": "allowCreate"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; "appendTo": { "alias": "appendTo"; "required": false; "isSignal": true; }; "groupBy": { "alias": "groupBy"; "required": false; "isSignal": true; }; "remoteSearch": { "alias": "remoteSearch"; "required": false; "isSignal": true; }; "minTermLength": { "alias": "minTermLength"; "required": false; "isSignal": true; }; "debounceMs": { "alias": "debounceMs"; "required": false; "isSignal": true; }; "typeToSearchText": { "alias": "typeToSearchText"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; "createOption": "createOption"; "search": "search"; "opened": "opened"; "closed": "closed"; "cleared": "cleared"; "added": "added"; "removed": "removed"; "selectionChange": "selectionChange"; }, ["optionTemplate"], never, true, never>;
3607
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlAutocomplete, "ol-autocomplete", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "valueKey": { "alias": "valueKey"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "showAsterisk": { "alias": "showAsterisk"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "errorText": { "alias": "errorText"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "allowCreate": { "alias": "allowCreate"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; "appendTo": { "alias": "appendTo"; "required": false; "isSignal": true; }; "menuClass": { "alias": "menuClass"; "required": false; "isSignal": true; }; "matchTriggerWidth": { "alias": "matchTriggerWidth"; "required": false; "isSignal": true; }; "groupBy": { "alias": "groupBy"; "required": false; "isSignal": true; }; "remoteSearch": { "alias": "remoteSearch"; "required": false; "isSignal": true; }; "minTermLength": { "alias": "minTermLength"; "required": false; "isSignal": true; }; "debounceMs": { "alias": "debounceMs"; "required": false; "isSignal": true; }; "typeToSearchText": { "alias": "typeToSearchText"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; "createOption": "createOption"; "search": "search"; "opened": "opened"; "closed": "closed"; "cleared": "cleared"; "added": "added"; "removed": "removed"; "selectionChange": "selectionChange"; }, ["optionTemplate"], never, true, never>;
3041
3608
  }
3042
3609
 
3043
3610
  declare class OlKbd {
@@ -3106,6 +3673,16 @@ type OlModalRole = 'dialog' | 'alertdialog';
3106
3673
  declare class OlModal {
3107
3674
  private readonly focusTrap;
3108
3675
  private readonly destroyRef;
3676
+ private readonly overlayContainer;
3677
+ /**
3678
+ * Hàm gỡ khỏi ngăn xếp Escape. `null` khi modal đang đóng.
3679
+ *
3680
+ * Modal KHÔNG gọi `overlayContainer.attach()` — nó nằm trong cây DOM của
3681
+ * chính nó chứ không treo ra `<body>`. Nhưng vẫn phải vào ngăn xếp Escape,
3682
+ * nếu không lớp nổi mở bên trong nó (datepicker, dropdown) sẽ kéo nó đóng
3683
+ * theo khi nhấn Escape.
3684
+ */
3685
+ private releaseEscape;
3109
3686
  private readonly panel;
3110
3687
  /**
3111
3688
  * Bậc thẻ tiêu đề (h1–h6). Mặc định 3 — giữ nguyên bậc đang chạy.
@@ -3132,7 +3709,6 @@ declare class OlModal {
3132
3709
  constructor();
3133
3710
  readonly placementClasses: _angular_core.Signal<"items-start justify-start p-4" | "items-start justify-center p-4" | "items-start justify-end p-4" | "items-end justify-start p-4" | "items-end justify-center p-4" | "items-end justify-end p-4" | "items-center justify-center p-4">;
3134
3711
  readonly sizeClasses: _angular_core.Signal<"max-w-md" | "max-w-4xl" | "max-w-6xl" | "max-w-7xl" | "max-w-2xl">;
3135
- handleEscapeKey(): void;
3136
3712
  onBackdropClick(event: MouseEvent): void;
3137
3713
  show(): void;
3138
3714
  hide(): void;
@@ -3157,11 +3733,44 @@ declare class OlModalHeader {
3157
3733
  */
3158
3734
  readonly headingLevel: _angular_core.InputSignal<1 | 2 | 3 | 4 | 5 | 6>;
3159
3735
  readonly heading: _angular_core.InputSignal<string>;
3736
+ /**
3737
+ * Icon đầu tiêu đề. Rỗng thì KHÔNG dựng khung — không để lại thẻ trống.
3738
+ *
3739
+ * Figma `Type=Icon + Heading & helper text` (#1149:71412) vẽ khung 48px bo
3740
+ * 12px, icon 28px bên trong. Đó đúng là `ol-icon-shape` size `lg`, nên dùng
3741
+ * lại thay vì tự vẽ khung.
3742
+ *
3743
+ * `color="white"` chứ KHÔNG `gray`: Figma bind nền
3744
+ * `bg-neutral-primary-medium` (trắng) kèm viền `border-default-medium`, mà
3745
+ * trong `ol-icon-shape` chỉ nhánh `white` có cặp đó — `gray` ra nền xám đặc
3746
+ * và KHÔNG viền. Đo /modal lượt đầu: nền rgb(243,244,246), viền 0px.
3747
+ */
3748
+ readonly icon: _angular_core.InputSignal<string>;
3749
+ /**
3750
+ * Mô tả phụ dưới tiêu đề.
3751
+ *
3752
+ * Tên `description` theo `ol-alert-dialog` — thứ dựng trên chính `ol-modal`
3753
+ * — và `ol-empty`. `helperText` mang nghĩa "chú thích dưới ô nhập" ở họ form
3754
+ * (16 component dùng), đặt cho tiêu đề hộp thoại là lệch nghĩa.
3755
+ */
3756
+ readonly description: _angular_core.InputSignal<string>;
3757
+ /** @deprecated Dùng `description`. Giữ để không phá mã đang chạy. */
3160
3758
  readonly supportingText: _angular_core.InputSignal<string>;
3759
+ /** @deprecated Dùng `description`. Tên cũ của `ol-drawer-header`. */
3760
+ readonly helperText: _angular_core.InputSignal<string>;
3761
+ /**
3762
+ * Cái nào được TRUYỀN thì thắng — không phụ thuộc giá trị.
3763
+ *
3764
+ * `ol-badge` từng hoà giải hai input cùng nghĩa bằng cách so với giá trị mặc
3765
+ * định (`theme` thắng trừ khi nó bằng `'gray'`), nên truyền cả hai thì một
3766
+ * cái bị nuốt im lặng. Ở đây chuỗi rỗng là "không truyền", nên chỉ cần lấy
3767
+ * cái đầu tiên khác rỗng.
3768
+ */
3769
+ readonly descriptionText: _angular_core.Signal<string>;
3161
3770
  readonly showClose: _angular_core.InputSignal<boolean>;
3162
3771
  readonly closeClick: _angular_core.OutputEmitterRef<void>;
3163
3772
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlModalHeader, never>;
3164
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlModalHeader, "ol-modal-header", never, { "headingLevel": { "alias": "headingLevel"; "required": false; "isSignal": true; }; "heading": { "alias": "heading"; "required": false; "isSignal": true; }; "supportingText": { "alias": "supportingText"; "required": false; "isSignal": true; }; "showClose": { "alias": "showClose"; "required": false; "isSignal": true; }; }, { "closeClick": "closeClick"; }, never, ["*"], true, never>;
3773
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlModalHeader, "ol-modal-header", never, { "headingLevel": { "alias": "headingLevel"; "required": false; "isSignal": true; }; "heading": { "alias": "heading"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; "supportingText": { "alias": "supportingText"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "showClose": { "alias": "showClose"; "required": false; "isSignal": true; }; }, { "closeClick": "closeClick"; }, never, ["*"], true, never>;
3165
3774
  }
3166
3775
 
3167
3776
  /** Khớp trục `Type` của Figma ol-modal-footer (node 1149:71444). */
@@ -3172,9 +3781,15 @@ type ModalFooterLayout = 'buttons' | 'three-buttons' | 'buttons-links' | 'pagina
3172
3781
  * Nút thật do nơi dùng truyền vào qua <ng-content>; component lo bố cục.
3173
3782
  *
3174
3783
  * KHÔNG lắp vào slot `[footer]` của `ol-modal`. Slot đó ĐÃ nằm sẵn trong một
3175
- * khung `p-5 border-t bg-neutral-secondary` do chính `ol-modal` dựng, nên lắp
3176
- * thêm component này vào là LỒNG HAI KHUNG. Đo /modal (2026-09-16): đệm cộng
3177
- * dồn 20 + 20 = 40px, hai viền trên 1px chồng nhau, nền lặp hai lần.
3784
+ * khung do chính `ol-modal` dựng, nên lắp thêm component này vào là LỒNG HAI
3785
+ * KHUNG. Đo /modal (2026-09-16, khi cả hai còn `p-5`): đệm cộng dồn 20 + 20 =
3786
+ * 40px, hai viền trên chồng nhau, nền lặp hai lần.
3787
+ *
3788
+ * Từ 2026-09-23 cả hai khung đều PHẲNG (không viền, không nền) nên lỗi lồng
3789
+ * khung KHÓ THẤY hơn hẳn — chỉ còn đệm cộng dồn 12 + 12 = 24px. Vì vậy cả hai
3790
+ * khung mang thuộc tính dấu `footer-frame`, và spec đếm số thẻ mang dấu đó
3791
+ * phải bằng 1. Neo vào dấu chứ không vào `p-3`: đổi con số đệm là việc thiết
3792
+ * kế bình thường, không nên làm đỏ một spec vốn gác chuyện khác.
3178
3793
  *
3179
3794
  * Dùng nó khi tự dựng hộp thoại (không qua `ol-modal`), hoặc trong `ol-drawer`
3180
3795
  * — `ol-drawer` chỉ có `<ng-content />` TRẦN nên ghép vừa khít. Đó là lý do
@@ -3234,22 +3849,53 @@ declare class OlNavDropdown {
3234
3849
  readonly alignRight: _angular_core.InputSignal<boolean>;
3235
3850
  readonly isOpen: _angular_core.WritableSignal<boolean>;
3236
3851
  private readonly elementRef;
3852
+ private readonly overlayContainer;
3853
+ constructor();
3237
3854
  toggle(): void;
3238
3855
  close(): void;
3239
3856
  handleClickOutside(event: MouseEvent): void;
3240
- handleEscape(): void;
3241
3857
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlNavDropdown, never>;
3242
3858
  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
3859
  }
3244
3860
 
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';
3861
+ type PaginationType = 'default' | 'arrows' | 'dropdown-buttons' | 'text-buttons' | 'two-buttons' | 'buttons-helper' | 'with-dropdown' | 'with-input' | 'simple-input'
3862
+ /**
3863
+ * `grouped` và `separated` trước chỉ có ở `PaginationVariant`. Thêm vào đây
3864
+ * để người dùng chuyển từ `variant` (đã `@deprecated`) sang `type` mà không
3865
+ * mất giá trị nào — nếu không, bỏ `variant` là bỏ luôn hai bố cục này.
3866
+ *
3867
+ * Chúng là BÍ DANH bố cục, không phải bố cục mới: `grouped` ≡ `default`,
3868
+ * `separated` ≡ `text-buttons`. Xem `effectiveType`.
3869
+ */
3870
+ | 'grouped' | 'separated';
3871
+ /** @deprecated Dùng `PaginationType`. Nay hai kiểu đã trùng nhau hoàn toàn. */
3872
+ type PaginationVariant = PaginationType;
3247
3873
  declare class OlPagination {
3248
3874
  readonly type: _angular_core.InputSignal<PaginationType>;
3249
- readonly variant: _angular_core.InputSignal<PaginationVariant>;
3875
+ /**
3876
+ * @deprecated Dùng `type`. Giữ cho mã ngoài đang gọi — sẽ bỏ ở bản major sau.
3877
+ *
3878
+ * 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
3879
+ * rồi không biết cái nào thắng. `effectiveType` bên dưới xử lý tường minh
3880
+ * (`type` tường minh luôn thắng), nhưng bề mặt API vẫn nên còn một cái.
3881
+ *
3882
+ * `grouped`/`separated` chỉ có ở `variant` nên chúng đã được thêm vào
3883
+ * `PaginationType`: `grouped` ≡ `default`, `separated` ≡ `text-buttons`.
3884
+ *
3885
+ * KHÔNG xoá ngay: `@edmicro/olkit` đã publish lên npm công khai (1.1.1), bỏ
3886
+ * input công khai là breaking change cho consumer mà repo này không đo được.
3887
+ * Theo đúng nếp `@deprecated` đã có sẵn ở `onPageSizeChangeEvent` bên dưới.
3888
+ */
3889
+ readonly variant: _angular_core.InputSignal<PaginationType>;
3250
3890
  readonly total: _angular_core.InputSignal<number>;
3251
3891
  readonly pageSize: _angular_core.ModelSignal<number>;
3252
3892
  readonly pageIndex: _angular_core.ModelSignal<number>;
3893
+ /**
3894
+ * @deprecated Dùng `pageIndex`. Giữ cho mã ngoài đang gọi — bỏ ở major sau.
3895
+ *
3896
+ * Cùng lý do với `variant`: hai input một trục. Xem chú thích `trangHienTai`
3897
+ * bên dưới về cách gộp hai giá trị mà không xung đột.
3898
+ */
3253
3899
  readonly currentPage: _angular_core.ModelSignal<number>;
3254
3900
  readonly totalPagesInput: _angular_core.InputSignal<number | null>;
3255
3901
  readonly pageSizeOptions: _angular_core.InputSignal<number[]>;
@@ -3364,14 +4010,18 @@ declare class OlPopover {
3364
4010
  /**
3365
4011
  * Đẩy panel ra `<body>` thay vì để trong cây DOM của component.
3366
4012
  *
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`.
4013
+ * **Mặc định `'body'`** (từ bản này). Trước đây mặc định `null` — panel nằm
4014
+ * trong cây DOM của component, nên hễ đặt vào modal / drawer / bảng có
4015
+ * `overflow: hidden` là bị cắt, và người dùng phải tự biết mà bật. Đo
4016
+ * /modal: khung modal có `z-index` + `backdrop-filter` nên TẠO STACKING
4017
+ * CONTEXT — z-index của lớp nổi không thoát ra được — và `overflow: hidden`
4018
+ * 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
4019
+ * cái thứ hai. Xem chú thích đầy đủ trong `OlOverlayContainer`.
3372
4020
  *
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.
4021
+ * Đặt `null` để quay về hành vi cũ: cần khi CSS của nơi dùng nhắm vào panel
4022
+ * qua cây DOM (`.form-cua-toi ol-select .panel`) — selector kiểu đó chết khi
4023
+ * panel treo ra ngoài. Cùng tên và cùng ý nghĩa với `appendTo` của ng-select
4024
+ * và PrimeNG.
3375
4025
  */
3376
4026
  readonly appendTo: _angular_core.InputSignal<"body" | null>;
3377
4027
  readonly placement: _angular_core.InputSignal<PopoverPlacement>;
@@ -3391,7 +4041,7 @@ declare class OlPopover {
3391
4041
  private readonly overlayContainer;
3392
4042
  private cleanupAutoUpdate;
3393
4043
  /** Hàm trả panel về chỗ cũ khi đóng. */
3394
- private goPanel;
4044
+ private detachPanel;
3395
4045
  constructor();
3396
4046
  toggle(): void;
3397
4047
  open(): void;
@@ -3475,6 +4125,140 @@ declare class OlProgress {
3475
4125
  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
4126
  }
3477
4127
 
4128
+ /**
4129
+ * Thanh tiến trình mảnh ở mép trên trang — kiểu nprogress / YouTube.
4130
+ *
4131
+ * Dùng cho việc **không biết trước bao lâu**: chuyển route, tải dữ liệu nền.
4132
+ * Khác `ol-progress` (thanh trong luồng, biết phần trăm chính xác) và khác
4133
+ * `ol-loading-overlay` (phủ và CHẶN thao tác).
4134
+ *
4135
+ * Là SERVICE chứ không phải component đặt trong template, theo nếp
4136
+ * `OlToastService`: nơi gọi thường là router guard hay HTTP interceptor, không
4137
+ * phải template.
4138
+ *
4139
+ * ```ts
4140
+ * // router guard
4141
+ * progressBar.start();
4142
+ * // ... điều hướng xong
4143
+ * progressBar.done();
4144
+ *
4145
+ * // HTTP interceptor — `done()` trong `finalize` để lỗi cũng tắt thanh
4146
+ * intercept(req: HttpRequest<unknown>, next: HttpHandler) {
4147
+ * this.pb.start();
4148
+ * return next.handle(req).pipe(finalize(() => this.pb.done()));
4149
+ * }
4150
+ * ```
4151
+ *
4152
+ * `<ol-progress-bar-top />` đặt một lần ở app root để vẽ.
4153
+ *
4154
+ * **Figma chưa vẽ component này** (node `3283:24173` chỉ có thanh trong luồng).
4155
+ * Đã ghi vào `FIGMA-CAN-VE-THEM.md`; dựng theo thông lệ phổ biến và dùng token
4156
+ * sẵn có, sẽ đối chiếu lại khi thiết kế vẽ xong.
4157
+ */
4158
+ declare class OlProgressBarService {
4159
+ /** Đếm số việc đang chạy, KHÔNG phải cờ bật/tắt. */
4160
+ private readonly activeCount;
4161
+ private readonly progressValue;
4162
+ private timer;
4163
+ /** Thanh có đang hiện. */
4164
+ readonly visible: _angular_core.Signal<boolean>;
4165
+ /** Phần trăm hiện tại (0–100). */
4166
+ readonly percent: _angular_core.Signal<number>;
4167
+ /**
4168
+ * Bắt đầu một việc.
4169
+ *
4170
+ * Đếm việc thay vì đặt cờ: hai lời gọi HTTP song song thì `done()` của cái
4171
+ * xong trước KHÔNG được tắt thanh khi cái kia còn chạy. Cờ boolean sẽ tắt
4172
+ * sớm và thanh nhấp nháy.
4173
+ */
4174
+ start(): void;
4175
+ /**
4176
+ * 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.
4177
+ */
4178
+ done(): void;
4179
+ /** Dừng mọi việc và ẩn ngay — dùng khi huỷ điều hướng. */
4180
+ reset(): void;
4181
+ private clearTimer;
4182
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlProgressBarService, never>;
4183
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<any>;
4184
+ }
4185
+
4186
+ /**
4187
+ * Vẽ thanh tiến trình mép trên trang. Đặt MỘT LẦN ở app root.
4188
+ *
4189
+ * ```html
4190
+ * <!-- app.html -->
4191
+ * <ol-progress-bar-top />
4192
+ * <router-outlet />
4193
+ * ```
4194
+ *
4195
+ * Trạng thái do `OlProgressBarService` giữ — component này chỉ đọc. Xem chú
4196
+ * thích đầy đủ ở service về lý do tách hai phần.
4197
+ *
4198
+ * `z-toast` (100) là bậc cao nhất trong thang z-index của lib: thanh này phải
4199
+ * thấy được cả khi modal (80) đang mở, vì điều hướng và tải nền vẫn xảy ra
4200
+ * trong lúc modal mở.
4201
+ */
4202
+ declare class OlProgressBarTop {
4203
+ readonly service: OlProgressBarService;
4204
+ /** Màu thanh. Mặc định `brand` theo Figma `ol-progress` (#3283:24173). */
4205
+ readonly color: _angular_core.InputSignal<"danger" | "brand" | "success" | "warning">;
4206
+ readonly barClasses: _angular_core.Signal<"bg-warning-surface" | "bg-danger-surface" | "bg-brand" | "bg-success">;
4207
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlProgressBarTop, never>;
4208
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlProgressBarTop, "ol-progress-bar-top", never, { "color": { "alias": "color"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
4209
+ }
4210
+
4211
+ /**
4212
+ * Lớp phủ mờ + spinner, **CHẶN** thao tác trong lúc tải hoặc lưu.
4213
+ *
4214
+ * Khác ba thứ đã có trong lib:
4215
+ * - `ol-spinner` — vòng xoay đơn lẻ, không phủ, không chặn
4216
+ * - `ol-skeleton` — THAY CHỖ nội dung, không phủ lên
4217
+ * - `ol-table-loading` — skeleton hàng bảng, chỉ cho bảng
4218
+ *
4219
+ * Hai chế độ qua input `fullscreen`:
4220
+ *
4221
+ * ```html
4222
+ * <!-- phủ MỘT VÙNG: cha PHẢI có `relative` -->
4223
+ * <div class="relative">
4224
+ * <ol-loading-overlay [loading]="dangTai()" text="Đang tải..." />
4225
+ * <ol-table ... />
4226
+ * </div>
4227
+ *
4228
+ * <!-- phủ TOÀN MÀN -->
4229
+ * <ol-loading-overlay [loading]="dangLuu()" [fullscreen]="true" text="Đang lưu..." />
4230
+ * ```
4231
+ *
4232
+ * **Ràng buộc của chế độ phủ vùng**: phần tử cha phải có `position: relative`
4233
+ * (hoặc `absolute`/`fixed`). Thiếu nó thì `absolute inset-0` neo theo tổ tiên
4234
+ * được định vị gần nhất — có thể là cả trang. Đây là ràng buộc của CSS, lib
4235
+ * không tự thêm `relative` vào cha được vì không với tới phần tử đó.
4236
+ *
4237
+ * **Figma chưa vẽ component này** (node `3283:24173` chỉ có thanh tiến trình
4238
+ * trong luồng). Đã ghi vào `FIGMA-CAN-VE-THEM.md`; dựng bằng token sẵn có
4239
+ * (`bg-overlay-backdrop` mà modal/drawer/command đang dùng) và sẽ đối chiếu
4240
+ * lại khi thiết kế vẽ xong.
4241
+ */
4242
+ declare class OlLoadingOverlay {
4243
+ readonly loading: _angular_core.InputSignal<boolean>;
4244
+ /** Chữ dưới spinner. Rỗng thì chỉ hiện spinner. */
4245
+ readonly text: _angular_core.InputSignal<string>;
4246
+ /**
4247
+ * Phủ toàn màn thay vì phủ vùng cha.
4248
+ *
4249
+ * Phủ vùng (mặc định) dùng cho bảng đang tải, form đang lưu. Phủ toàn màn
4250
+ * dùng khi không được cho bấm bất cứ gì khác.
4251
+ */
4252
+ readonly fullscreen: _angular_core.InputSignal<boolean>;
4253
+ /** Theo nếp `ol-table-loading`: nhãn mặc định tiếng Việt. */
4254
+ readonly ariaLabel: _angular_core.InputSignal<string>;
4255
+ /** Spinner to hơn ở chế độ toàn màn cho cân với diện tích phủ. */
4256
+ readonly spinnerSize: _angular_core.Signal<"base" | "lg">;
4257
+ 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">;
4258
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlLoadingOverlay, never>;
4259
+ 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>;
4260
+ }
4261
+
3478
4262
  /**
3479
4263
  * Trục `Type` của Figma `ol-rating` (#1149:82444). Code trước đây chỉ làm
3480
4264
  * được `Default`, hai kiểu kia Figma vẽ đủ mà code chưa có.
@@ -3498,6 +4282,16 @@ declare class OlRating {
3498
4282
  /** Nhãn hai đầu ở `radio-input` — Figma: "Không hài lòng" / "Rất hài lòng". */
3499
4283
  readonly minLabel: _angular_core.InputSignal<string>;
3500
4284
  readonly maxLabel: _angular_core.InputSignal<string>;
4285
+ /**
4286
+ * Tên trợ năng cho nhóm chấm điểm (`role="radiogroup"` ở nhánh
4287
+ * `radio-input`).
4288
+ *
4289
+ * Trước đây nhóm lấy tên từ `minLabel()` — sai ngữ nghĩa: đó là nhãn ĐẦU
4290
+ * THANG ("Rất tệ"), không phải tên của thang, và nó ĐÃ hiện thành chữ ngay
4291
+ * bên cạnh nên trình đọc màn hình đọc lặp. `minLabel()` giữ làm dự phòng để
4292
+ * mã đang chạy không mất tên khi chưa kịp đổi.
4293
+ */
4294
+ readonly ariaLabel: _angular_core.InputSignal<string>;
3501
4295
  /** Tên nhóm radio; tự sinh khi nơi gọi không truyền. */
3502
4296
  readonly name: _angular_core.InputSignal<string>;
3503
4297
  /** Giá trị đang chọn ở `radio-input`; `null` là chưa chọn. */
@@ -3532,7 +4326,193 @@ declare class OlRating {
3532
4326
  radioClass(v: number): string;
3533
4327
  select(v: number): void;
3534
4328
  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>;
4329
+ 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>;
4330
+ }
4331
+
4332
+ /**
4333
+ * Kiểu dữ liệu cho `ol-app-nav` — menu điều hướng Ôn Luyện.
4334
+ *
4335
+ * Tách riêng khỏi component để nơi dùng khai được danh sách menu ở bất kỳ đâu:
4336
+ * hằng số trong mã, tệp JSON, hoặc trả về từ API theo quyền tài khoản. Component
4337
+ * KHÔNG nhúng sẵn danh sách nào — ba quyền Học sinh · Giáo viên · Phụ huynh đều
4338
+ * là dữ liệu truyền vào.
4339
+ *
4340
+ * Figma `ol-app-nav` (#5303:30109) mô tả ba quyền, nhưng đó là NỘI DUNG mẫu,
4341
+ * không phải ràng buộc của component. Thêm quyền thứ tư chỉ là thêm một mảng.
4342
+ */
4343
+ /** Một mục trong menu. */
4344
+ interface AppNavItem {
4345
+ /**
4346
+ * Khoá định danh, dùng để đánh dấu mục đang chọn.
4347
+ *
4348
+ * Tách khỏi `route` vì một mục có thể không có đường dẫn (mở hộp thoại, gọi
4349
+ * hàm), và hai mục có thể trỏ cùng route với tham số khác nhau.
4350
+ */
4351
+ id: string;
4352
+ /** Chữ hiển thị. Ở trạng thái thu gọn, chữ này thành tên trợ năng. */
4353
+ label: string;
4354
+ /**
4355
+ * Nhãn rút gọn cho chỗ hẹp — navbar đáy trên mobile.
4356
+ *
4357
+ * Đo ở 375px: navbar 5 ô, mỗi ô 75px. Nhãn dài hơn ~10 ký tự bị cắt cụt
4358
+ * ("Lịch học trực tuyến" cần 95px), và 8/15 nhãn của ba quyền mẫu vượt
4359
+ * ngưỡng đó — tức không phải ca hiếm.
4360
+ *
4361
+ * Rút tự động bằng thuật toán ra chữ vô nghĩa, nên để nơi dùng tự đặt.
4362
+ * Bỏ trống thì dùng `label`; chỗ hẹp vẫn nên đặt cho chắc.
4363
+ *
4364
+ * KHÔNG thay `label` ở tooltip và trợ năng — hai chỗ đó vẫn đọc tên đầy đủ.
4365
+ */
4366
+ shortLabel?: string;
4367
+ /** Tên icon theo `OlIconService` — dạng ngắn (`star`) hoặc có tiền tố bộ. */
4368
+ icon: string;
4369
+ /** Đường dẫn router. Bỏ trống thì mục chỉ phát sự kiện `itemClick`. */
4370
+ route?: string;
4371
+ /** Huy hiệu đếm bên phải nhãn, ví dụ số tin chưa đọc. */
4372
+ badge?: string | number;
4373
+ /** Mờ 40% và không bấm được — theo Figma State=Disabled. */
4374
+ disabled?: boolean;
4375
+ }
4376
+ /**
4377
+ * Một nhóm mục. Figma chia menu làm hai phần, ngăn bằng một đường kẻ:
4378
+ *
4379
+ * `main` phần trên — nghiệp vụ chính, chiếm hết chỗ trống
4380
+ * `footer` phần dưới — tiện ích, luôn nằm sát thẻ tài khoản
4381
+ *
4382
+ * Dùng `section` chứ không dựa vào thứ tự mảng: nơi dùng khai một danh sách
4383
+ * phẳng, component tự tách hai vùng.
4384
+ */
4385
+ interface AppNavGroup {
4386
+ section: 'main' | 'footer';
4387
+ items: AppNavItem[];
4388
+ }
4389
+ /** Thẻ tài khoản dưới cùng. */
4390
+ interface AppNavUser {
4391
+ name: string;
4392
+ /** Dòng phụ — Figma dùng email, nhưng nơi dùng đặt gì cũng được. */
4393
+ subtitle?: string;
4394
+ /** Ảnh đại diện. Bỏ trống thì `ol-avatar` tự dựng chữ cái đầu từ `name`. */
4395
+ avatarSrc?: string;
4396
+ }
4397
+ /**
4398
+ * Trọn bộ dữ liệu một menu — đúng thứ nơi dùng nạp từ JSON.
4399
+ *
4400
+ * ```ts
4401
+ * const menuHocSinh: AppNavConfig = {
4402
+ * groups: [
4403
+ * { section: 'main', items: [{ id: 'lop-hoc-so', label: 'Lớp học số', icon: 'calendar-add' }] },
4404
+ * { section: 'footer', items: [{ id: 'ho-tro', label: 'Hướng dẫn & Hỗ trợ', icon: 'question-circle' }] },
4405
+ * ],
4406
+ * user: { name: 'Nguyễn Nhật Linh', subtitle: 'hocsinh.demo@onluyen.vn' },
4407
+ * };
4408
+ * ```
4409
+ */
4410
+ interface AppNavConfig {
4411
+ groups: AppNavGroup[];
4412
+ user?: AppNavUser;
4413
+ }
4414
+
4415
+ /**
4416
+ * `ol-app-nav` — menu điều hướng dọc của Ôn Luyện.
4417
+ *
4418
+ * Figma `ol-app-nav` (#5303:30109), 6 biến thể: 3 quyền × 2 trạng thái.
4419
+ *
4420
+ * ## Khác gì `ol-sidebar`
4421
+ *
4422
+ * `ol-sidebar` là thanh bên ĐA DỤNG: nền sáng theo token, nhận nội dung tuỳ ý
4423
+ * qua `ng-content`, có menu con accordion. Component này là thanh điều hướng
4424
+ * RIÊNG của sản phẩm Ôn Luyện: nền gradient navy cố định, cấu trúc chốt sẵn
4425
+ * (logo → menu chính → đường kẻ → menu phụ → thẻ tài khoản), nhận dữ liệu qua
4426
+ * một đối tượng thay vì qua slot.
4427
+ *
4428
+ * Không gộp hai cái làm một: gộp thì `ol-sidebar` phải mang theo nền gradient
4429
+ * và bố cục ba vùng mà 90% nơi dùng không cần.
4430
+ *
4431
+ * ## Dữ liệu từ ngoài, không nhúng cứng
4432
+ *
4433
+ * Ba quyền trong Figma là NỘI DUNG mẫu. Component nhận `config` — nơi dùng nạp
4434
+ * từ hằng số, tệp JSON, hay API đều được. Thêm quyền thứ tư chỉ là thêm một
4435
+ * mảng, không phải sửa component.
4436
+ *
4437
+ * ## Nền gradient nằm ngoài thang token
4438
+ *
4439
+ * Figma chốt `linear-gradient(164.52deg, #445A9A, #0F2977)`. Hai màu này không
4440
+ * có trong `primitives.css` và cũng không nên có — chúng là bản sắc của MỘT
4441
+ * sản phẩm, không phải bậc màu dùng chung. Khai biến cục bộ trong template.
4442
+ *
4443
+ * Hệ quả: chữ trên nền này luôn trắng, không đổi theo chế độ sáng/tối. Đúng
4444
+ * như Figma mô tả ("dùng trên nền tối, chữ và biểu tượng đều `text-white`").
4445
+ */
4446
+ declare class OlAppNav {
4447
+ /** Danh sách menu và thẻ tài khoản. Xem `AppNavConfig`. */
4448
+ readonly config: _angular_core.InputSignal<AppNavConfig>;
4449
+ /**
4450
+ * Thu gọn còn 70px, chỉ hiện icon.
4451
+ *
4452
+ * `model()` để nơi dùng bind hai chiều `[(collapsed)]` — nút thu gọn thường
4453
+ * nằm ngoài component (trên thanh header), nhưng cũng có nơi tự điều khiển.
4454
+ */
4455
+ readonly collapsed: _angular_core.ModelSignal<boolean>;
4456
+ /**
4457
+ * Mục đang chọn, theo `id`.
4458
+ *
4459
+ * Dùng khi menu KHÔNG đi qua router — ví dụ một trang đơn đổi nội dung bằng
4460
+ * signal. Có `route` thì `routerLinkActive` tự lo, không cần đặt cái này.
4461
+ */
4462
+ readonly activeId: _angular_core.InputSignal<string | null>;
4463
+ /** Ảnh logo. Bỏ trống thì dựng chữ "Ôn luyện" bằng text. */
4464
+ readonly logoSrc: _angular_core.InputSignal<string>;
4465
+ /** Chiều rộng khi mở. Figma: 270px. */
4466
+ readonly width: _angular_core.InputSignal<string>;
4467
+ /** Chiều rộng khi thu gọn. Figma: 70px. */
4468
+ readonly collapsedWidth: _angular_core.InputSignal<string>;
4469
+ /** Phát khi bấm một mục. Mục có `route` thì router đã lo điều hướng. */
4470
+ readonly itemClick: _angular_core.OutputEmitterRef<AppNavItem>;
4471
+ /** Phát khi bấm thẻ tài khoản — nơi dùng mở menu đổi tài khoản, đăng xuất. */
4472
+ readonly userClick: _angular_core.OutputEmitterRef<void>;
4473
+ /**
4474
+ * Hiện thanh kéo (rail) ở mép phải để thu gọn / mở lại.
4475
+ *
4476
+ * Tắt mặc định vì nó chỉ hợp khi menu đứng cạnh nội dung trong một layout
4477
+ * thật. Dùng menu rời (như trang docs) thì thanh này lơ lửng vô nghĩa.
4478
+ */
4479
+ readonly showRail: _angular_core.InputSignal<boolean>;
4480
+ /** Chữ trên tooltip của thanh kéo, đổi theo trạng thái. */
4481
+ readonly railLabel: _angular_core.Signal<"Mở rộng" | "Thu gọn">;
4482
+ /** Mục của phần trên. */
4483
+ readonly mainItems: _angular_core.Signal<AppNavItem[]>;
4484
+ /** Mục của phần dưới, nằm sát thẻ tài khoản. */
4485
+ readonly footerItems: _angular_core.Signal<AppNavItem[]>;
4486
+ /** Đường kẻ chỉ vẽ khi phần dưới có mục — không thì thừa một vạch. */
4487
+ readonly showDivider: _angular_core.Signal<boolean>;
4488
+ readonly navWidth: _angular_core.Signal<string>;
4489
+ /**
4490
+ * Class cho một mục, theo trạng thái.
4491
+ *
4492
+ * Figma `ol-app-sidebar-item` (#5301:338): cao 44, bo 12, đệm 10, khe 8.
4493
+ *
4494
+ * Initial trong suốt
4495
+ * Hover nền brand/600
4496
+ * Active nền bg-warning, chữ đậm
4497
+ * Disabled mờ 40%
4498
+ *
4499
+ * Gộp vào MỘT chuỗi thay vì nhiều `[class.x]`: hai binding cùng tên thuộc
4500
+ * tính thì cái sau thắng, cái trước mất im lặng.
4501
+ */
4502
+ itemClasses(item: AppNavItem): string;
4503
+ /**
4504
+ * Chữ cái đầu cho ảnh đại diện, khi không có ảnh.
4505
+ *
4506
+ * `ol-avatar` IN NGUYÊN chuỗi truyền vào `text` chứ không tự cắt — truyền cả
4507
+ * họ tên thì nó hiện trọn tên trong vòng tròn 40px. Lấy chữ cái đầu của HAI
4508
+ * từ CUỐI: tên Việt để họ trước, "Nguyễn Nhật Linh" phải ra "NL" chứ không
4509
+ * phải "NN".
4510
+ */
4511
+ readonly userInitials: _angular_core.Signal<string>;
4512
+ onItemClick(item: AppNavItem): void;
4513
+ toggle(): void;
4514
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlAppNav, never>;
4515
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlAppNav, "ol-app-nav", never, { "config": { "alias": "config"; "required": true; "isSignal": true; }; "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; "activeId": { "alias": "activeId"; "required": false; "isSignal": true; }; "logoSrc": { "alias": "logoSrc"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "collapsedWidth": { "alias": "collapsedWidth"; "required": false; "isSignal": true; }; "showRail": { "alias": "showRail"; "required": false; "isSignal": true; }; }, { "collapsed": "collapsedChange"; "itemClick": "itemClick"; "userClick": "userClick"; }, never, never, true, never>;
3536
4516
  }
3537
4517
 
3538
4518
  declare class OlSidebar {
@@ -3541,11 +4521,26 @@ declare class OlSidebar {
3541
4521
  readonly collapsedWidth: _angular_core.InputSignal<string>;
3542
4522
  readonly collapsible: _angular_core.InputSignal<boolean>;
3543
4523
  readonly bordered: _angular_core.InputSignal<boolean>;
4524
+ /**
4525
+ * Hiện thanh kéo ở mép phải để thu gọn / mở rộng.
4526
+ *
4527
+ * Mặc định TẮT: `toggle()` vốn để nơi dùng tự dựng nút bên ngoài, bật sẵn
4528
+ * sẽ thêm một nút vào mọi sidebar đang chạy mà không ai yêu cầu.
4529
+ *
4530
+ * Thanh này ẩn dưới `md` — ở màn hẹp sidebar thường được thay bằng ngăn kéo
4531
+ * trượt, và một vạch 8px cạnh mép màn hình rất khó trúng bằng ngón tay.
4532
+ */
4533
+ readonly showRail: _angular_core.InputSignal<boolean>;
4534
+ /**
4535
+ * Chữ trên tooltip của thanh kéo — theo trạng thái SẼ TỚI, không phải trạng
4536
+ * thái đang đứng. Người dùng đọc nhãn để biết bấm vào thì được gì.
4537
+ */
4538
+ readonly railLabel: _angular_core.Signal<"Mở rộng" | "Thu gọn">;
3544
4539
  readonly currentWidth: _angular_core.Signal<string>;
3545
4540
  readonly sidebarClasses: _angular_core.Signal<string>;
3546
4541
  toggle(): void;
3547
4542
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlSidebar, never>;
3548
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlSidebar, "ol-sidebar", never, { "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "collapsedWidth": { "alias": "collapsedWidth"; "required": false; "isSignal": true; }; "collapsible": { "alias": "collapsible"; "required": false; "isSignal": true; }; "bordered": { "alias": "bordered"; "required": false; "isSignal": true; }; }, { "collapsed": "collapsedChange"; }, never, ["[slot=header], [header]", "*", "[slot=footer], [footer]"], true, never>;
4543
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlSidebar, "ol-sidebar", never, { "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "collapsedWidth": { "alias": "collapsedWidth"; "required": false; "isSignal": true; }; "collapsible": { "alias": "collapsible"; "required": false; "isSignal": true; }; "bordered": { "alias": "bordered"; "required": false; "isSignal": true; }; "showRail": { "alias": "showRail"; "required": false; "isSignal": true; }; }, { "collapsed": "collapsedChange"; }, never, ["[slot=header], [header]", "*", "[slot=footer], [footer]"], true, never>;
3549
4544
  }
3550
4545
 
3551
4546
  declare class OlSidebarItem {
@@ -3557,7 +4552,7 @@ declare class OlSidebarItem {
3557
4552
  readonly href: _angular_core.InputSignal<string | null>;
3558
4553
  readonly icon: _angular_core.InputSignal<string | null>;
3559
4554
  readonly badge: _angular_core.InputSignal<string | number | null>;
3560
- readonly badgeTheme: _angular_core.InputSignal<BadgeTheme>;
4555
+ readonly badgeColor: _angular_core.InputSignal<BadgeColor>;
3561
4556
  readonly active: _angular_core.InputSignal<boolean>;
3562
4557
  readonly disabled: _angular_core.InputSignal<boolean>;
3563
4558
  readonly dot: _angular_core.InputSignal<boolean>;
@@ -3567,7 +4562,7 @@ declare class OlSidebarItem {
3567
4562
  */
3568
4563
  readonly itemClasses: _angular_core.Signal<string>;
3569
4564
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlSidebarItem, never>;
3570
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlSidebarItem, "ol-sidebar-item", never, { "routerLink": { "alias": "routerLink"; "required": false; "isSignal": true; }; "routerLinkActive": { "alias": "routerLinkActive"; "required": false; "isSignal": true; }; "routerLinkActiveOptions": { "alias": "routerLinkActiveOptions"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "badge": { "alias": "badge"; "required": false; "isSignal": true; }; "badgeTheme": { "alias": "badgeTheme"; "required": false; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "dot": { "alias": "dot"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
4565
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlSidebarItem, "ol-sidebar-item", never, { "routerLink": { "alias": "routerLink"; "required": false; "isSignal": true; }; "routerLinkActive": { "alias": "routerLinkActive"; "required": false; "isSignal": true; }; "routerLinkActiveOptions": { "alias": "routerLinkActiveOptions"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "badge": { "alias": "badge"; "required": false; "isSignal": true; }; "badgeColor": { "alias": "badgeColor"; "required": false; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "dot": { "alias": "dot"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
3571
4566
  }
3572
4567
 
3573
4568
  declare class OlSidebarGroup {
@@ -3580,7 +4575,7 @@ declare class OlSidebarSubmenu {
3580
4575
  readonly label: _angular_core.InputSignal<string>;
3581
4576
  readonly icon: _angular_core.InputSignal<string | null>;
3582
4577
  readonly badge: _angular_core.InputSignal<string | number | null>;
3583
- readonly badgeTheme: _angular_core.InputSignal<BadgeTheme>;
4578
+ readonly badgeColor: _angular_core.InputSignal<BadgeColor>;
3584
4579
  readonly isOpen: _angular_core.ModelSignal<boolean>;
3585
4580
  readonly active: _angular_core.InputSignal<boolean>;
3586
4581
  /**
@@ -3590,7 +4585,7 @@ declare class OlSidebarSubmenu {
3590
4585
  readonly triggerClasses: _angular_core.Signal<string>;
3591
4586
  toggle(): void;
3592
4587
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlSidebarSubmenu, never>;
3593
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlSidebarSubmenu, "ol-sidebar-submenu", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "badge": { "alias": "badge"; "required": false; "isSignal": true; }; "badgeTheme": { "alias": "badgeTheme"; "required": false; "isSignal": true; }; "isOpen": { "alias": "isOpen"; "required": false; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; }, { "isOpen": "isOpenChange"; }, never, ["*"], true, never>;
4588
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlSidebarSubmenu, "ol-sidebar-submenu", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "badge": { "alias": "badge"; "required": false; "isSignal": true; }; "badgeColor": { "alias": "badgeColor"; "required": false; "isSignal": true; }; "isOpen": { "alias": "isOpen"; "required": false; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; }, { "isOpen": "isOpenChange"; }, never, ["*"], true, never>;
3594
4589
  }
3595
4590
 
3596
4591
  /**
@@ -3694,6 +4689,10 @@ interface SpeedDialAction {
3694
4689
 
3695
4690
  declare class OlSpeedDial {
3696
4691
  private readonly elementRef;
4692
+ private readonly overlayContainer;
4693
+ /** Hàm gỡ khỏi ngăn xếp Escape. `null` khi đang đóng. */
4694
+ private releaseEscape;
4695
+ constructor();
3697
4696
  readonly direction: _angular_core.InputSignal<SpeedDialDirection>;
3698
4697
  readonly type: _angular_core.InputSignal<SpeedDialType>;
3699
4698
  readonly trigger: _angular_core.InputSignal<SpeedDialTrigger>;
@@ -3724,7 +4723,6 @@ declare class OlSpeedDial {
3724
4723
  onMouseLeave(): void;
3725
4724
  onBackdropMouseEnter(): void;
3726
4725
  onDocumentClick(event: MouseEvent): void;
3727
- onEscape(): void;
3728
4726
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlSpeedDial, never>;
3729
4727
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlSpeedDial, "ol-speed-dial", never, { "direction": { "alias": "direction"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "trigger": { "alias": "trigger"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "tooltip": { "alias": "tooltip"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "backdrop": { "alias": "backdrop"; "required": false; "isSignal": true; }; "closeOnClickOutside": { "alias": "closeOnClickOutside"; "required": false; "isSignal": true; }; "closeOnEscape": { "alias": "closeOnEscape"; "required": false; "isSignal": true; }; "actions": { "alias": "actions"; "required": false; "isSignal": true; }; "customClass": { "alias": "class"; "required": false; "isSignal": true; }; "isOpen": { "alias": "isOpen"; "required": false; "isSignal": true; }; }, { "isOpen": "isOpenChange"; "onOpen": "onOpen"; "onClose": "onClose"; "actionClick": "actionClick"; }, never, ["*"], true, never>;
3730
4728
  }
@@ -3792,7 +4790,7 @@ declare class OlSpinner {
3792
4790
  * - white: border-white
3793
4791
  * - success: border-emerald-600 (ngoài Figma)
3794
4792
  * - warning: border-yellow-500 (ngoài Figma)
3795
- * - danger: border-rose-600 (ngoài Figma)
4793
+ * - danger: text-fg-danger (token — trùng rose-600 ở chế độ sáng)
3796
4794
  * - gray: border-gray-400 (ngoài Figma)
3797
4795
  */
3798
4796
  readonly color: _angular_core.InputSignal<OlSpinnerColor>;
@@ -3930,15 +4928,32 @@ declare class OlTabItem {
3930
4928
  readonly icon: _angular_core.InputSignal<string | null>;
3931
4929
  readonly avatar: _angular_core.InputSignal<string | null>;
3932
4930
  readonly badge: _angular_core.InputSignal<string | number | null>;
3933
- readonly badgeTheme: _angular_core.InputSignal<BadgeTheme>;
4931
+ readonly badgeColor: _angular_core.InputSignal<BadgeColor>;
3934
4932
  readonly disabled: _angular_core.InputSignal<boolean>;
3935
4933
  readonly displayLabel: _angular_core.Signal<string>;
3936
4934
  readonly displaySubtitle: _angular_core.Signal<string>;
3937
4935
  protected readonly hostClass: _angular_core.Signal<"contents" | "flex min-h-0 w-full flex-1 flex-col">;
3938
4936
  private readonly parentTabs;
3939
4937
  readonly isActive: _angular_core.Signal<boolean>;
4938
+ /**
4939
+ * Có dựng `role="tabpanel"` hay không (BUG-026).
4940
+ *
4941
+ * Ở chế độ `mobile` với các variant `default`/`pills`/`border-bottom`/
4942
+ * `underline`, `ol-tabs` render một `<select>` thay cho hàng tab — KHÔNG
4943
+ * sinh `role="tab"` nào. Panel vẫn khai `role="tabpanel"` +
4944
+ * `aria-labelledby="<tabId>"` thì nó thành **panel mồ côi**: vai trò đòi
4945
+ * phải có một `tab` điều khiển nó, mà `aria-labelledby` lại trỏ vào một id
4946
+ * KHÔNG TỒN TẠI trong DOM.
4947
+ *
4948
+ * 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
4949
+ * tab không hề tồn tại. Chế độ `<select>` đã tự có tên ("Chọn tab") nên bỏ
4950
+ * hẳn vai trò là đúng, không phải mất mát.
4951
+ *
4952
+ * Không có `parentTabs` (dùng `ol-tab-item` rời) thì giữ nguyên hành vi cũ.
4953
+ */
4954
+ readonly hasTabRole: _angular_core.Signal<boolean>;
3940
4955
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlTabItem, never>;
3941
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTabItem, "ol-tab-item", never, { "tabId": { "alias": "tabId"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "helper": { "alias": "helper"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "avatar": { "alias": "avatar"; "required": false; "isSignal": true; }; "badge": { "alias": "badge"; "required": false; "isSignal": true; }; "badgeTheme": { "alias": "badgeTheme"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
4956
+ 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; }; "badgeColor": { "alias": "badgeColor"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
3942
4957
  }
3943
4958
 
3944
4959
  /**
@@ -4313,12 +5328,12 @@ declare class OlBottomNavItem {
4313
5328
  readonly icon: _angular_core.InputSignal<string>;
4314
5329
  readonly label: _angular_core.InputSignal<string>;
4315
5330
  readonly badge: _angular_core.InputSignal<string | number | null>;
4316
- readonly badgeTheme: _angular_core.InputSignal<BadgeTheme>;
5331
+ readonly badgeColor: _angular_core.InputSignal<BadgeColor>;
4317
5332
  readonly isCenterAction: _angular_core.InputSignal<boolean>;
4318
5333
  readonly active: _angular_core.InputSignal<boolean>;
4319
5334
  readonly itemClasses: _angular_core.Signal<string>;
4320
5335
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlBottomNavItem, never>;
4321
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlBottomNavItem, "ol-bottom-nav-item", never, { "routerLink": { "alias": "routerLink"; "required": false; "isSignal": true; }; "routerLinkActive": { "alias": "routerLinkActive"; "required": false; "isSignal": true; }; "routerLinkActiveOptions": { "alias": "routerLinkActiveOptions"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "badge": { "alias": "badge"; "required": false; "isSignal": true; }; "badgeTheme": { "alias": "badgeTheme"; "required": false; "isSignal": true; }; "isCenterAction": { "alias": "isCenterAction"; "required": false; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
5336
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlBottomNavItem, "ol-bottom-nav-item", never, { "routerLink": { "alias": "routerLink"; "required": false; "isSignal": true; }; "routerLinkActive": { "alias": "routerLinkActive"; "required": false; "isSignal": true; }; "routerLinkActiveOptions": { "alias": "routerLinkActiveOptions"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "badge": { "alias": "badge"; "required": false; "isSignal": true; }; "badgeColor": { "alias": "badgeColor"; "required": false; "isSignal": true; }; "isCenterAction": { "alias": "isCenterAction"; "required": false; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
4322
5337
  }
4323
5338
 
4324
5339
  type StepState = 'initial' | 'active' | 'completed' | 'error' | 'disabled';
@@ -4345,6 +5360,20 @@ type StepperOrientation = 'horizontal' | 'vertical';
4345
5360
  type StepperBreakpoint = 'desktop' | 'mobile';
4346
5361
  declare class OlStepper {
4347
5362
  readonly currentStep: _angular_core.ModelSignal<number>;
5363
+ /**
5364
+ * Tên đọc được cho nút của một bước.
5365
+ *
5366
+ * Nút bước chỉ chiếu icon ở 3/4 nhánh (`completed` -> check, `error` ->
5367
+ * info-circle, có `icon` riêng -> icon đó); chỉ nhánh cuối mới có số thứ tự
5368
+ * làm chữ. `ol-icon` không tự sinh tên đọc được nên **bước càng hoàn thành
5369
+ * thì càng mất tên** — ngược hẳn mong đợi.
5370
+ *
5371
+ * Đo `/stepper` bằng accessibility tree: **39/61 nút câm** (BUG-013 mục 1).
5372
+ *
5373
+ * Nhãn gồm số thứ tự + tiêu đề (nếu có) + trạng thái, để người dùng trình
5374
+ * đọc màn hình biết đang ở đâu mà không phải đoán từ icon.
5375
+ */
5376
+ stepAriaLabel(i: number): string;
4348
5377
  readonly variant: _angular_core.InputSignal<StepperVariant>;
4349
5378
  readonly orientation: _angular_core.InputSignal<StepperOrientation>;
4350
5379
  readonly breakpoint: _angular_core.InputSignal<StepperBreakpoint>;
@@ -4368,7 +5397,7 @@ interface OlTreeNode {
4368
5397
  icon?: string;
4369
5398
  count?: number;
4370
5399
  badge?: string;
4371
- badgeTheme?: 'brand' | 'green' | 'yellow' | 'red' | 'gray';
5400
+ badgeColor?: 'brand' | 'green' | 'yellow' | 'red' | 'gray';
4372
5401
  children?: OlTreeNode[];
4373
5402
  expanded?: boolean;
4374
5403
  selected?: boolean;
@@ -4380,6 +5409,41 @@ type OlTreeSelectionMode = 'single' | 'multiple';
4380
5409
  type OlTreeToggleIcon = 'chevron' | 'plus-minus';
4381
5410
  declare class OlTree {
4382
5411
  readonly nodes: _angular_core.ModelSignal<OlTreeNode[]>;
5412
+ /**
5413
+ * Template tuỳ biến cho nội dung mỗi hàng, khai bằng `#nodeTemplate`:
5414
+ *
5415
+ * ```html
5416
+ * <ol-tree [nodes]="nodes">
5417
+ * <ng-template #nodeTemplate let-node>
5418
+ * <ol-icon [name]="node.icon" />
5419
+ * <span class="truncate flex-1">{{ node.label }}</span>
5420
+ * </ng-template>
5421
+ * </ol-tree>
5422
+ * ```
5423
+ *
5424
+ * Theo đúng nếp `@ContentChild('optionTemplate')` của `ol-select`. Template
5425
+ * được truyền xuống MỌI cấp của cây đệ quy.
5426
+ */
5427
+ nodeTemplate?: TemplateRef<unknown>;
5428
+ /**
5429
+ * Nút/hành động gắn ở CUỐI mỗi hàng, khai bằng `#actionsTemplate`:
5430
+ *
5431
+ * ```html
5432
+ * <ol-tree [nodes]="nodes">
5433
+ * <ng-template #actionsTemplate let-node>
5434
+ * <ol-button size="xs" color="ghost" (btnClick)="sua(node)">Sửa</ol-button>
5435
+ * </ng-template>
5436
+ * </ol-tree>
5437
+ * ```
5438
+ *
5439
+ * Khác `#nodeTemplate` ở chỗ nó **cộng thêm** chứ không thay thế: icon, nhãn,
5440
+ * count và badge mặc định vẫn nguyên. `#nodeTemplate` là all-or-nothing —
5441
+ * bật lên thì phải tự vẽ lại cả bốn thứ đó, nên chỉ muốn thêm một nút ở cuối
5442
+ * mà phải dùng nó là chép thừa.
5443
+ *
5444
+ * Dùng được ĐỒNG THỜI với `#nodeTemplate`: nút vẫn hiện ở cả hai nhánh.
5445
+ */
5446
+ actionsTemplate?: TemplateRef<unknown>;
4383
5447
  readonly checkable: _angular_core.InputSignal<boolean>;
4384
5448
  readonly selectionMode: _angular_core.InputSignal<OlTreeSelectionMode>;
4385
5449
  readonly bordered: _angular_core.InputSignal<boolean>;
@@ -4413,7 +5477,7 @@ declare class OlTree {
4413
5477
  */
4414
5478
  handleKeydown(event: KeyboardEvent): void;
4415
5479
  /** Cha trực tiếp của một node, `null` nếu node ở mức gốc. */
4416
- private timCha;
5480
+ private findParent;
4417
5481
  handleNodeClick(node: OlTreeNode): void;
4418
5482
  handleNodeCheck(event: {
4419
5483
  node: OlTreeNode;
@@ -4426,13 +5490,33 @@ declare class OlTree {
4426
5490
  private setSelectedNodeSingle;
4427
5491
  private cloneTree;
4428
5492
  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>;
5493
+ 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", "actionsTemplate"], never, true, never>;
4430
5494
  }
4431
5495
 
4432
5496
  type OlTreeNodeSelectionMode = 'single' | 'multiple';
4433
5497
  type OlTreeNodeToggleIcon = 'chevron' | 'plus-minus';
4434
5498
  declare class OlTreeNodeItem {
4435
5499
  readonly node: _angular_core.InputSignal<OlTreeNode>;
5500
+ /**
5501
+ * Template tuỳ biến cho PHẦN NỘI DUNG của hàng (icon · nhãn · count · badge).
5502
+ *
5503
+ * Truyền qua `input` chứ không `inject` ngược cây: node vốn đã nhận 7 input
5504
+ * truyền xuống đệ quy, thêm một cái là đúng nếp sẵn có — còn `inject` ngược
5505
+ * là dựng thêm một cơ chế mới cho cùng một việc.
5506
+ *
5507
+ * Chỉ thay NỘI DUNG, KHÔNG thay cả hàng. Thẻ `role="treeitem"` cùng
5508
+ * `aria-level` / `aria-expanded` / roving tabindex và nút mở rộng, checkbox
5509
+ * đều do lib giữ. Cho người dùng vẽ cả hàng là mất ARIA vừa làm đúng chuẩn ở
5510
+ * BUG-017 — họ không có cách nào biết phải khai lại những thứ đó.
5511
+ */
5512
+ readonly nodeTemplate: _angular_core.InputSignal<TemplateRef<unknown> | null>;
5513
+ /**
5514
+ * Template cho phần CUỐI hàng — nút, menu, hành động.
5515
+ *
5516
+ * Cộng thêm chứ không thay thế: dựng NGOÀI cặp `@if (nodeTemplate())` nên
5517
+ * hiện ở cả hai nhánh, dùng chung được với `nodeTemplate`.
5518
+ */
5519
+ readonly actionsTemplate: _angular_core.InputSignal<TemplateRef<unknown> | null>;
4436
5520
  readonly checkable: _angular_core.InputSignal<boolean>;
4437
5521
  readonly selectionMode: _angular_core.InputSignal<OlTreeNodeSelectionMode>;
4438
5522
  readonly toggleIcon: _angular_core.InputSignal<OlTreeNodeToggleIcon>;
@@ -4456,6 +5540,23 @@ declare class OlTreeNodeItem {
4456
5540
  protected readonly ariaLevel: _angular_core.Signal<number>;
4457
5541
  /** `aria-expanded` CHỈ đặt cho node có con; node lá để `null`. */
4458
5542
  protected readonly ariaExpanded: _angular_core.Signal<boolean | null>;
5543
+ /**
5544
+ * Ngữ cảnh cho CẢ HAI template. Một `computed` dùng chung chứ không viết
5545
+ * object thẳng trong template: viết thẳng thì mỗi lần dò thay đổi lại sinh
5546
+ * một object mới, `ngTemplateOutlet` thấy khác tham chiếu nên dựng lại nội
5547
+ * dung — mất cả trạng thái bên trong nút người dùng đặt vào.
5548
+ *
5549
+ * `$implicit` cho `let-node`; `node` cho `let-n="node"`. Ba khoá còn lại bắt
5550
+ * kịp `ol-autocomplete` (đã có `selected`) và mở thêm `level`/`expanded` —
5551
+ * hàng cây thường cần biết mình sâu bao nhiêu để thụt chữ hay đổi cỡ.
5552
+ */
5553
+ protected readonly tplContext: _angular_core.Signal<{
5554
+ $implicit: OlTreeNode;
5555
+ node: OlTreeNode;
5556
+ level: number;
5557
+ selected: boolean;
5558
+ expanded: boolean;
5559
+ }>;
4459
5560
  constructor();
4460
5561
  /**
4461
5562
  * Nút cha có con chọn một phần thì ô của nó phải là dấu gạch.
@@ -4476,7 +5577,7 @@ declare class OlTreeNodeItem {
4476
5577
  handleClick(): void;
4477
5578
  handleCheck(checked: boolean): void;
4478
5579
  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>;
5580
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OlTreeNodeItem, "ol-tree-node", never, { "node": { "alias": "node"; "required": true; "isSignal": true; }; "nodeTemplate": { "alias": "nodeTemplate"; "required": false; "isSignal": true; }; "actionsTemplate": { "alias": "actionsTemplate"; "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
5581
  }
4481
5582
 
4482
5583
  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,86 +6046,34 @@ declare class OlJumbotron {
4945
6046
  readonly subtitle: _angular_core.InputSignal<string>;
4946
6047
  readonly badgeText: _angular_core.InputSignal<string>;
4947
6048
  readonly variant: _angular_core.InputSignal<JumbotronVariant>;
4948
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlJumbotron, never>;
4949
- 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
- }
4951
-
4952
- /**
4953
- * Abstract base class cho các overlay components (Modal, Drawer, Popover)
4954
- * Cung cấp functionality chung cho việc mở/đóng và xử lý interactions
4955
- */
4956
- declare abstract class OlOverlayBase implements OnDestroy {
4957
- /**
4958
- * Signal tracking trạng thái mở/đóng của overlay
4959
- */
4960
- isOpen: WritableSignal<boolean>;
4961
- /**
4962
- * Signal cho việc hiển thị backdrop
4963
- */
4964
- backdrop: WritableSignal<boolean>;
4965
- /**
4966
- * Signal cho phép đóng overlay khi nhấn Escape
4967
- */
4968
- closeOnEscape: WritableSignal<boolean>;
4969
- /**
4970
- * Signal cho phép đóng overlay khi click vào backdrop
4971
- */
4972
- closeOnBackdrop: WritableSignal<boolean>;
4973
- /**
4974
- * Signal tracking animation state
4975
- */
4976
- protected animating: WritableSignal<boolean>;
4977
- /** Handle của setTimeout animation — lưu lại để huỷ khi destroy, tránh
4978
- * callback chạy trên instance đã chết. */
4979
- private animationTimer;
4980
- constructor();
4981
- /**
4982
- * Mở overlay với optional animation
4983
- */
4984
- open(): void;
4985
- /**
4986
- * Đóng overlay với optional animation
4987
- */
4988
- close(): void;
4989
- /**
4990
- * Toggle trạng thái overlay
4991
- */
4992
- toggle(): void;
4993
- /**
4994
- * Xử lý sự kiện click vào backdrop
4995
- */
4996
- handleBackdropClick(event: MouseEvent): void;
4997
- /**
4998
- * Xử lý sự kiện nhấn phím Escape
4999
- */
5000
- protected handleEscape: (event: KeyboardEvent) => void;
5001
- /**
5002
- * Lifecycle hook được gọi khi overlay mở
5003
- * Override trong subclass để customize behavior
5004
- */
5005
- protected onOpen(): void;
5006
6049
  /**
5007
- * Lifecycle hook được gọi khi overlay đóng
5008
- * Override trong subclass để customize behavior
5009
- */
5010
- protected onClose(): void;
5011
- /**
5012
- * Thêm event listener cho phím Escape
5013
- */
5014
- private addEscapeListener;
5015
- /**
5016
- * Xóa event listener cho phím Escape
5017
- */
5018
- private removeEscapeListener;
5019
- /**
5020
- * Cleanup khi component bị destroy.
5021
- * Gọi tự động qua ngOnDestroy — trước đây hàm này tồn tại nhưng không ai gọi,
5022
- * nên component destroy lúc đang mở sẽ để lại listener keydown trên document.
6050
+ * Class cho badge — đổi theo `variant` vì NỀN đổi theo `variant`.
6051
+ *
6052
+ * Bản cũ khoá cứng `bg-white/10 … text-fg-yellow` cho mọi biến thể. Hợp lý
6053
+ * trên nền tối, nhưng `variant="light"` có nền gần trắng nên chữ vàng chỉ
6054
+ * đạt **1.58:1** — dưới xa ngưỡng WCAG 4.5 (BUG-013 mục 3b). Đo cả 5 badge
6055
+ * trên `/jumbotron`:
6056
+ *
6057
+ * | nền | tỷ lệ |
6058
+ * |------------------------|----------|
6059
+ * | `light` rgb(249,250,251) | **1.58** |
6060
+ * | `brand` rgb(43,65,130) | **4.45** (thiếu 0.05) |
6061
+ * | `dark` rgb(16,24,40) | 8.18 ✅ |
6062
+ *
6063
+ * `brand` cũng trượt (4.45, thiếu 0.05) — sát ngưỡng vẫn là trượt. Sửa bằng
6064
+ * cách làm nền badge TỐI hơn (`bg-white/5` thay `bg-white/10`) để giữ sắc
6065
+ * vàng, thay vì đổi màu chữ: đo `bg-white/5` ra **5.15**.
6066
+ *
6067
+ * Đã thử `text-fg-yellow-strong`, `text-yellow-200`, `text-yellow-100` —
6068
+ * cả ba là **class chết**, `getComputedStyle` trả về đen. `text-white` đạt
6069
+ * 7.3 nhưng mất sắc vàng của badge.
6070
+ *
6071
+ * Nền badge cũng phải đổi ở `light`: `bg-white/10` trên nền sáng gần như
6072
+ * vô hình.
5023
6073
  */
5024
- protected cleanup(): void;
5025
- ngOnDestroy(): void;
5026
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlOverlayBase, never>;
5027
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<OlOverlayBase, never, never, {}, {}, never, never, true, never>;
6074
+ 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">;
6075
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlJumbotron, never>;
6076
+ 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>;
5028
6077
  }
5029
6078
 
5030
6079
  /**
@@ -5065,6 +6114,17 @@ declare class OlFocusTrapService {
5065
6114
  private previousActiveElement;
5066
6115
  private trapElement;
5067
6116
  private focusableElements;
6117
+ /**
6118
+ * ĐẾM overlay đang mở, không dùng cờ bật/tắt.
6119
+ *
6120
+ * Mở modal rồi mở `ol-command` từ trong nó: đóng cái sau KHÔNG được nhả khoá
6121
+ * 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
6122
+ * vẫn hiện — cùng lớp lỗi với `OlProgressBarService`.
6123
+ */
6124
+ private openOverlayCount;
6125
+ /** Giá trị `overflow`/`paddingRight` của `body` trước khi khoá, để trả lại. */
6126
+ private prevOverflow;
6127
+ private prevPaddingRight;
5068
6128
  /**
5069
6129
  * Selector cho các elements có thể focus được
5070
6130
  */
@@ -5079,6 +6139,33 @@ declare class OlFocusTrapService {
5079
6139
  * Release focus trap và restore focus về element trước đó
5080
6140
  */
5081
6141
  release(): void;
6142
+ /**
6143
+ * Khoá cuộn trang nền.
6144
+ *
6145
+ * `aria-modal="true"` là lời hứa với trợ năng rằng phần còn lại của trang
6146
+ * không tương tác được. Đo `/modal` trước khi sửa: overlay mở mà
6147
+ * `body.overflow` vẫn `visible`, lăn chuột làm nền trôi 1317 → 1717 trong
6148
+ * khi overlay đứng yên — người dùng mất ngữ cảnh (BUG-027).
6149
+ *
6150
+ * Khoá trên phần tử CUỘN THẬT (`document.scrollingElement`), KHÔNG mặc định
6151
+ * là `body`. Đo `/modal`: `scrollingElement` là `<html>` với
6152
+ * `scrollTop 1817`, còn `body.scrollTop` = 0 — đặt `overflow: hidden` trên
6153
+ * `body` thì nền vẫn cuộn y nguyên (đã thử: 1317 → 1717).
6154
+ *
6155
+ * Bù `padding-right` bằng đúng bề rộng thanh cuộn: đặt `overflow: hidden`
6156
+ * 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ù.
6157
+ *
6158
+ * KHÔNG dùng `position: fixed`: cách đó phải tự lưu và trả lại `scrollY`,
6159
+ * quên là trang nhảy về đầu khi đóng. `overflow: hidden` giữ nguyên vị trí.
6160
+ */
6161
+ private lockScroll;
6162
+ /**
6163
+ * Phần tử cuộn thật của trang. `document.scrollingElement` là `<html>` ở chế
6164
+ * độ chuẩn và `<body>` ở chế độ quirks — không đoán, hỏi trình duyệt.
6165
+ */
6166
+ private scrollingEl;
6167
+ /** Nhả khoá khi overlay CUỐI cùng đóng. */
6168
+ private unlockScroll;
5082
6169
  /**
5083
6170
  * Update danh sách focusable elements trong trap element
5084
6171
  */
@@ -5113,6 +6200,24 @@ interface PositioningOptions {
5113
6200
  shift?: boolean;
5114
6201
  arrow?: HTMLElement;
5115
6202
  middleware?: Middleware[];
6203
+ /**
6204
+ * Giới hạn kích thước floating element theo chỗ trống còn lại.
6205
+ *
6206
+ * Cần cho popup CAO (lịch, danh sách dài): `flip` chỉ lật sang phía đối diện
6207
+ * khi phía đó rộng hơn, nhưng nếu CẢ HAI phía đều không đủ thì popup vẫn tràn
6208
+ * khỏi màn. `size` cho nó một trần thật để cuộn bên trong.
6209
+ */
6210
+ size?: {
6211
+ /** Chiều cao tối thiểu, dưới mức này thì `flip` mới đổi phía. */
6212
+ minHeight?: number;
6213
+ /** Chừa mép màn. */
6214
+ padding?: number;
6215
+ /** Gọi khi có kích thước khả dụng mới. */
6216
+ apply?: (args: {
6217
+ availableHeight: number;
6218
+ availableWidth: number;
6219
+ }) => void;
6220
+ };
5116
6221
  }
5117
6222
  /**
5118
6223
  * Return type cho positioned coordinates
@@ -5213,23 +6318,94 @@ declare class OlOverlayContainer {
5213
6318
  * `--z-toast: 100`), nên lớp nổi trong container luôn phủ mọi thứ vẽ theo
5214
6319
  * thang đó. Cùng mốc CDK và PrimeNG dùng.
5215
6320
  */
5216
- private zHienTai;
5217
- /** Container chung, tạo một lần rồi dùng lại. */
6321
+ private currentZ;
6322
+ /**
6323
+ * Container chung, tạo một lần rồi dùng lại.
6324
+ *
6325
+ * Tìm trong DOM trước khi tạo mới. Service khai `providedIn: 'root'` nên mỗi
6326
+ * injector dựng một instance với `host = null` — trong ứng dụng thật chỉ có
6327
+ * một injector, nhưng mỗi `TestBed` là một cái. Không tìm lại thì container
6328
+ * chồng chất: đo được 4 test `select-append-to.spec.ts` đỏ 1 trên 3 lần
6329
+ * chạy, vì chúng giả định CHỈ MỘT container.
6330
+ *
6331
+ * Dùng lại cũng đúng hơn về mặt hành vi: hai instance service không nên vẽ
6332
+ * ra hai lớp phủ chồng nhau.
6333
+ */
5218
6334
  getContainer(): HTMLElement;
5219
6335
  /**
5220
6336
  * 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
6337
  * lớp mở sau luôn nằm trên, kể cả khi mở modal trong modal.
5222
6338
  */
6339
+ nextZIndex(): number;
6340
+ /**
6341
+ * @deprecated Dùng `nextZIndex()`. Giữ lại vì đã publish trong
6342
+ * `@edmicro/olkit@1.1.1` — bỏ phương thức công khai là breaking change.
6343
+ */
5223
6344
  capZIndex(): number;
5224
6345
  /**
5225
6346
  * Chuyển một phần tử vào container và cấp z-index.
5226
6347
  * Trả về hàm hoàn tác — gọi khi lớp đóng để trả phần tử về chỗ cũ.
5227
6348
  */
6349
+ attach(el: HTMLElement): () => void;
6350
+ /**
6351
+ * Các lớp nổi ĐANG MỞ, xếp theo thứ tự mở — phần tử cuối là lớp trên cùng.
6352
+ *
6353
+ * Dùng mảng chứ không `Set`: thứ tự chính là thông tin cần, và một lớp không
6354
+ * thể mở hai lần nên không sợ trùng.
6355
+ */
6356
+ private readonly openLayers;
6357
+ /** Trình nghe Escape duy nhất, gắn khi có lớp đầu tiên mở. */
6358
+ private escapeListener;
6359
+ /**
6360
+ * Đăng ký một lớp nổi vừa mở. Trả hàm gỡ — gọi khi lớp đóng.
6361
+ *
6362
+ * ## Vì sao cần
6363
+ *
6364
+ * Trước đây mỗi component tự gắn `@HostListener('document:keydown.escape')`.
6365
+ * Không ai chặn lan truyền và không ai biết ai, nên **một phím Escape đóng
6366
+ * MỌI lớp đang mở**: mở datepicker trong modal rồi nhấn Escape thì cả hai
6367
+ * cùng đóng, người dùng mất luôn hộp thoại đang điền dở.
6368
+ *
6369
+ * Ở đây một trình nghe duy nhất, chỉ gọi `close()` của lớp **trên cùng**.
6370
+ * Nhấn lần nữa mới tới lớp dưới — đúng cách người dùng chờ đợi.
6371
+ *
6372
+ * ## Dùng thế nào
6373
+ *
6374
+ * ```ts
6375
+ * private releaseEscape: (() => void) | null = null;
6376
+ *
6377
+ * open(): void {
6378
+ * this.releaseEscape = this.overlayContainer.register(() => this.close());
6379
+ * }
6380
+ *
6381
+ * close(): void {
6382
+ * this.releaseEscape?.();
6383
+ * this.releaseEscape = null;
6384
+ * }
6385
+ * ```
6386
+ *
6387
+ * Hàm gỡ **an toàn khi gọi nhiều lần** — `close()` thường chạy cả từ phím
6388
+ * Escape lẫn từ nút đóng, và cả hai đường đều gọi nó.
6389
+ */
6390
+ register(close: () => void): () => void;
6391
+ /**
6392
+ * Gắn trình nghe khi có lớp đầu tiên, không gắn sẵn từ lúc dựng service.
6393
+ *
6394
+ * Không có lớp nào mở mà vẫn nghe phím là bắt mọi lần nhấn Escape của cả
6395
+ * ứng dụng — kể cả khi người dùng đang gõ trong ô nhập bình thường.
6396
+ */
6397
+ private ensureEscapeListener;
6398
+ /** Gỡ trình nghe khi lớp cuối cùng đóng — không để nó nghe vô ích. */
6399
+ private teardownEscapeListenerIfIdle;
6400
+ /**
6401
+ * @deprecated Dùng `attach()`. Giữ lại vì đã publish trong
6402
+ * `@edmicro/olkit@1.1.1` — bỏ phương thức công khai là breaking change.
6403
+ */
5228
6404
  gan(el: HTMLElement): () => void;
5229
6405
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OlOverlayContainer, never>;
5230
6406
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<any>;
5231
6407
  }
5232
6408
 
5233
- export { CHAT_BUBBLE_STATUS, MONTHS_FULL_EN, MONTHS_FULL_VI, MONTHS_SHORT_VI, OlAccordion, OlAccordionItem, OlAlert, OlAlertDialog, OlAspectRatio, OlAutocomplete, OlAvatar, OlAvatarGroup, OlBackdrop, OlBadge, OlBanner, OlBottomNav, OlBottomNavItem, OlBreadcrumb, OlBreadcrumbItem, OlButton, OlButtonGroup, OlCard, OlCarousel, OlCarouselSlide, OlChart, OlChatBubble, OlChatContainer, OlCheckbox, OlCheckboxRadioForm, OlCheckboxRadioGroup, OlClipboard, OlCollapse, OlCommand, OlDateRangePicker, OlDatepicker, OlDatepickerDirective, OlDatepickerFooter, OlDatepickerHeader, OlDatepickerPopup, OlDatepickerStateService, OlDayView, OlDeviceMockup, OlDownloadAppButton, OlDrawer, OlDrawerFooter, OlDrawerHeader, OlDropdown, OlDropdownHeader, OlDropdownItem, OlEmpty, OlFileInput, OlFloatingLabel, OlFocusTrapService, OlFooter, OlGallery, OlGalleryItem, OlHelper, OlIcon, OlIconService, OlIconShape, OlInput, OlInputCounter, OlJumbotron, OlKbd, OlLabel, OlListGroup, OlListGroupItem, OlModal, OlModalFooter, OlModalHeader, OlMonthView, OlNavDropdown, OlNavbar, OlNavbarItem, OlOtpInput, OlOverlayBase, OlOverlayContainer, OlPagination, OlPopover, OlPopoverContent, OlPositioningService, OlProgress, OlRadio, OlRange, OlRating, OlRichTextEditor, OlScrollArea, OlSelect, OlSeparator, OlSidebar, OlSidebarGroup, OlSidebarItem, OlSidebarSubmenu, OlSkeleton, OlSpeedDial, OlSpeedDialItem, OlSpinner, OlStepItem, OlStepper, OlTabItem, OlTable, OlTableLoading, OlTableRow, OlTabs, OlTagInput, OlTextarea, OlTimeline, OlTimelineItem, OlTimepicker, OlToast, OlToastContainer, OlToastService, OlToggle, OlToggleGroup, OlToggleInput, OlTooltip, OlTooltipDirective, OlTree, OlTreeNodeItem, OlYearView, WEEKDAYS_SHORT_EN, WEEKDAYS_SHORT_VI, boDau, cloneDate, endOfDay, formatDate, generateMonthMatrix, generateMonthsMatrix, generateYearsMatrix, isBetween, isDateDisabled, isSameDay, isSameMonth, isSameYear, isToday, parseDate, startOfDay };
5234
- export type { AccordionVariant, AlertColor, AlertDialogTone, AlertLayout, AlertRounded, AlertType, AlertVariant, AppDevice, AppTheme, AutocompleteOption, AutocompleteSize, AutocompleteState, AvatarShape, AvatarSize, AvatarStatus, BadgeShape, BadgeSize, BadgeTheme, BadgeType, BannerPosition, BannerVariant, BoxplotDataPoint, BreadcrumbItemStatus, BreadcrumbSeparator, BreadcrumbVariant, ButtonColor, ButtonGroupColor, ButtonGroupOrientation, ButtonGroupSize, ButtonSize, CardPadding, CardRadius, CardShadow, CardVariant, ChartDataPoint, ChartSeries, ChartType, ChatBubbleStatus, ChatSender, CheckboxSize, ChoiceControl, ChoiceControlPosition, ChoiceGroupLayout, ChoiceLayout, ChoiceShape, CommandGroup, CommandItem, DateCustomClass, DateRange, DatepickerPreset, DatepickerSelectionMode, DatepickerSize, DatepickerViewMode, DayCell, DeviceType, DrawerFooterLayout, DrawerPlacement, DrawerSize, DropdownAutoClose, DropdownPlacement, DropdownTrigger, EmptySize, FileInputSize, FloatingLabelSize, FloatingLabelState, FloatingLabelVariant, HeatmapData, HelperFeature, HelperRule, HelperSize, HelperState, IconColor, IconShapeColor, IconShapeSize, IconShapeType, IconSize, IconVariant, InputCounterSize, InputSize, InputState, JumbotronVariant, ListGroupType, ModalFooterLayout, ModalPlacement, ModalSize, MonthCell, OlAvatarStatusPosition, OlBottomNavPosition, OlButtonType, OlCheckboxRadioFormAlign, OlCheckboxRadioGroupRole, OlDateRangePickerVariant, OlGalleryGap, OlGalleryRatio, OlHelperType, OlModalBackdrop, OlModalRole, OlSkeletonAnimation, OlSkeletonAspectRatio, OlSkeletonRounded, OlSkeletonType, OlSpeedDialTooltipPosition, OlTimepickerAddonType, OlToggleInputAlign, OlTreeNode, OlTreeNodeSelectionMode, OlTreeNodeToggleIcon, OlTreeSelectionMode, OlTreeToggleIcon, OtpInputSize, PaginationType, PaginationVariant, PopoverPlacement, PopoverTrigger, PositionedCoordinates, PositioningOptions, ProgressColor, ProgressLabelPosition, ProgressSize, RadarIndicator, RadioSize, RatingType, ScatterDataPoint, ScatterSeries, ScrollAreaAxis, ScrollAreaScrollbar, SelectOption, SelectSize, SelectState, SeparatorOrientation, SpeedDialAction, SpeedDialColor, SpeedDialDirection, SpeedDialTrigger, SpeedDialType, StepState, StepperBreakpoint, StepperOrientation, StepperVariant, TabsSize, TabsVariant, TagInputSize, TextareaState, TimeObject, TimepickerFormat, TimepickerSize, TimepickerState, Toast, ToastConfig, ToastPosition, ToggleColor, TogglePosition, ToggleSize, YearCell };
6409
+ export { CHAT_BUBBLE_STATUS, MONTHS_FULL_EN, MONTHS_FULL_VI, MONTHS_SHORT_VI, OlAccordion, OlAccordionItem, OlAlert, OlAlertDialog, OlAppNav, 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, 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 };
6410
+ export type { AccordionVariant, AlertColor, AlertDialogTone, AlertLayout, AlertRounded, AlertType, AlertVariant, AppDevice, AppNavConfig, AppNavGroup, AppNavItem, AppNavUser, AppTheme, AutocompleteOption, AutocompleteSize, AutocompleteState, AvatarShape, AvatarSize, AvatarStatus, BadgeColor, 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
6411
  //# sourceMappingURL=edmicro-olkit.d.ts.map