@letsprogram/ng-oat 0.1.1 → 0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letsprogram/ng-oat",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Angular component library built on top of the Oat CSS framework — signals-first, lightweight, and accessible.",
5
5
  "license": "MIT",
6
6
  "author": "SashiKumar Yadav",
@@ -36,9 +36,16 @@
36
36
  "publishConfig": {
37
37
  "access": "public"
38
38
  },
39
- "module": "fesm2022/letsprogram-ng-oat.mjs",
40
- "typings": "types/letsprogram-ng-oat.d.ts",
41
39
  "exports": {
40
+ "./assets/*": {
41
+ "default": "./assets/*"
42
+ },
43
+ "./src/lib/styles/*": {
44
+ "default": "./src/lib/styles/*"
45
+ },
46
+ "./src/lib/tokens/*": {
47
+ "default": "./src/lib/tokens/*"
48
+ },
42
49
  "./package.json": {
43
50
  "default": "./package.json"
44
51
  },
@@ -50,5 +57,7 @@
50
57
  "types": "./types/letsprogram-ng-oat-cdk.d.ts",
51
58
  "default": "./fesm2022/letsprogram-ng-oat-cdk.mjs"
52
59
  }
53
- }
60
+ },
61
+ "module": "fesm2022/letsprogram-ng-oat.mjs",
62
+ "typings": "types/letsprogram-ng-oat.d.ts"
54
63
  }
@@ -5,25 +5,35 @@ import { FormCheckboxControl, FormValueControl, ValidationError, FieldState } fr
5
5
  /** Configuration for provideNgOat() */
6
6
  interface NgOatOptions {
7
7
  assets?: {
8
- /** How to load Oat CSS. 'auto' = 'link'. false = skip. */
9
- css?: 'auto' | 'link' | 'import' | false;
10
- /** How to load Oat JS. 'auto' = 'script'. false = skip. */
11
- js?: 'auto' | 'script' | false;
8
+ /**
9
+ * How to load Oat CSS at runtime.
10
+ * - `false` (default) CSS is loaded via angular.json styles array or styles.css import (recommended).
11
+ * - `'link'` dynamically inject a `<link>` tag (requires assets glob in angular.json).
12
+ */
13
+ css?: 'link' | false;
12
14
  };
13
- /** Register ot-tabs/ot-dropdown custom elements from Oat JS (default: true). */
14
- registerWebComponents?: boolean;
15
- /** Base path for vendored Oat assets (default: 'assets/oat'). */
15
+ /**
16
+ * Base path for vendored Oat assets when using dynamic CSS injection (`css: 'link'`).
17
+ * Only relevant if you opted into runtime injection AND configured an assets glob
18
+ * to copy files from `node_modules/@letsprogram/ng-oat/assets/oat` to this path.
19
+ * Default: `'assets/oat'`.
20
+ */
16
21
  basePath?: string;
17
22
  }
18
23
  /**
19
- * Provides Oat UI assets and initialisation.
24
+ * Provides ng-oat core initialisation.
25
+ *
26
+ * By default, no runtime asset injection occurs — add CSS via
27
+ * `angular.json` `styles` array or `@import` in `styles.css` (recommended).
28
+ * Oat JS is no longer needed; all behavior is handled natively by Angular components.
20
29
  *
21
- * Include in your app config providers array:
22
30
  * ```ts
23
- * provideNgOat() // auto-inject CSS + JS
24
- * provideNgOat({ assets: { css: false } }) // user imported CSS manually
25
- * provideNgOat({ basePath: '/custom/oat' }) // custom asset location
31
+ * provideNgOat() // recommended (CSS via angular.json / styles.css)
32
+ * provideNgOat({ assets: { css: 'link' } }) // opt-in runtime CSS injection *
26
33
  * ```
34
+ *
35
+ * \\* Runtime injection requires an assets glob in angular.json to copy files:
36
+ * `{ glob: '**\\/*', input: 'node_modules/@letsprogram/ng-oat/assets/oat', output: '/assets/oat' }`
27
37
  */
28
38
  declare function provideNgOat(options?: NgOatOptions): EnvironmentProviders;
29
39
 
@@ -319,10 +329,12 @@ declare class NgOatSidebar {
319
329
  }
320
330
 
321
331
  /**
322
- * Angular integration for Oat's <ot-tabs> WebComponent.
332
+ * Angular directive that provides native tab behaviour on any element
333
+ * containing `role="tablist"` > `role="tab"` buttons and sibling
334
+ * `role="tabpanel"` containers.
323
335
  *
324
- * Oat's WC handles all ARIA, keyboard nav, and panel toggling.
325
- * This directive provides Angular-friendly signals and event binding.
336
+ * Works without oat.js — handles tab activation, ARIA attributes,
337
+ * panel visibility and full keyboard navigation internally.
326
338
  *
327
339
  * Usage:
328
340
  * ```html
@@ -350,10 +362,13 @@ declare class NgOatTabs {
350
362
  index: number;
351
363
  tab: HTMLElement;
352
364
  }>;
353
- private tabChangeListener;
365
+ private tabEls;
366
+ private panelEls;
367
+ private cleanupFns;
354
368
  constructor();
355
369
  /** Programmatically select a tab by index */
356
370
  selectTab(index: number): void;
371
+ private activate;
357
372
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NgOatTabs, never>;
358
373
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NgOatTabs, "ot-tabs[ngOatTabs], [ngOatTabs]", ["ngOatTabs"], {}, { "ngOatTabChange": "ngOatTabChange"; }, never, never, true, never>;
359
374
  }
@@ -403,9 +418,9 @@ declare class NgOatDialog {
403
418
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NgOatDialog, "dialog[ngOatDialog]", ["ngOatDialog"], {}, { "ngOatDialogClose": "ngOatDialogClose"; }, never, never, true, never>;
404
419
  }
405
420
 
406
- /** Toast placement positions (matches Oat's toast system) */
421
+ /** Toast placement positions */
407
422
  type NgOatToastPlacement = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
408
- /** Toast variants (matches Oat's toast system) */
423
+ /** Toast variants */
409
424
  type NgOatToastVariant = 'info' | 'success' | 'danger' | 'warning';
410
425
  /** Options for showing a toast */
411
426
  interface NgOatToastOptions {
@@ -419,10 +434,11 @@ interface NgOatToastOptions {
419
434
  dismissible?: boolean;
420
435
  }
421
436
  /**
422
- * Angular service wrapping Oat's toast notification system.
437
+ * Angular service for Oat-styled toast notifications.
423
438
  *
424
- * Delegates to `window.ot.toast()` (provided by oat.min.js).
425
- * Falls back to a no-op if Oat JS is not loaded.
439
+ * Fully native implementation no dependency on oat.js.
440
+ * Uses the Oat CSS `.toast`, `.toast-container`, `data-entering/data-exiting`
441
+ * animation patterns, and popover API for stacking.
426
442
  *
427
443
  * Usage:
428
444
  * ```ts
@@ -436,7 +452,8 @@ interface NgOatToastOptions {
436
452
  declare class NgOatToast {
437
453
  private platformId;
438
454
  private doc;
439
- private get ot();
455
+ /** Cache of toast containers keyed by placement */
456
+ private containers;
440
457
  success(message: string, title?: string, options?: Omit<NgOatToastOptions, 'variant'>): void;
441
458
  info(message: string, title?: string, options?: Omit<NgOatToastOptions, 'variant'>): void;
442
459
  warning(message: string, title?: string, options?: Omit<NgOatToastOptions, 'variant'>): void;
@@ -444,7 +461,6 @@ declare class NgOatToast {
444
461
  show(message: string, title?: string, options?: NgOatToastOptions): HTMLElement | void;
445
462
  /**
446
463
  * Show a toast from a DOM element or template element.
447
- * For Angular TemplateRef, use showTemplate() instead.
448
464
  */
449
465
  showElement(element: HTMLElement, options?: NgOatToastOptions): void;
450
466
  /**
@@ -454,6 +470,12 @@ declare class NgOatToast {
454
470
  showTemplate(templateRef: TemplateRef<any>, vcr: ViewContainerRef, options?: NgOatToastOptions): void;
455
471
  /** Dismiss toasts. If placement given, only that position; otherwise all. */
456
472
  dismiss(placement?: NgOatToastPlacement): void;
473
+ /** Get or create a toast container for the given placement */
474
+ private getContainer;
475
+ /** Show a prepared element as a toast */
476
+ private showEl;
477
+ /** Remove a toast with exit animation */
478
+ private removeToast;
457
479
  /** Add a close button to a toast element */
458
480
  private addCloseButton;
459
481
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NgOatToast, never>;
@@ -835,6 +857,9 @@ declare class NgOatDropdownComponent {
835
857
  open(): void;
836
858
  close(): void;
837
859
  toggle(): void;
860
+ /** Keyboard navigation for menu items (ArrowDown/Up/Home/End/Escape) */
861
+ private onKeydown;
862
+ private doc;
838
863
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NgOatDropdownComponent, never>;
839
864
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<NgOatDropdownComponent, "ng-oat-dropdown", never, {}, { "openChange": "openChange"; }, never, ["[trigger]", "*"], true, never>;
840
865
  }
@@ -1504,6 +1529,169 @@ declare class NgOatCardCarousel implements AfterViewInit, OnDestroy {
1504
1529
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<NgOatCardCarousel, "ng-oat-card-carousel", never, { "heading": { "alias": "heading"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "items": { "alias": "items"; "required": false; "isSignal": true; }; "showSeeAll": { "alias": "showSeeAll"; "required": false; "isSignal": true; }; "scrollAmount": { "alias": "scrollAmount"; "required": false; "isSignal": true; }; }, { "seeAllClick": "seeAllClick"; "cardClick": "cardClick"; }, never, never, true, never>;
1505
1530
  }
1506
1531
 
1532
+ type NgOatToolbarColor = 'default' | 'primary' | 'accent';
1533
+ /**
1534
+ * Angular toolbar component — like mat-toolbar, built on Oat CSS.
1535
+ *
1536
+ * Renders a fixed-position `<nav data-topnav>` that leverages Oat's built-in
1537
+ * topnav styling (flex, border, shadow). Content-projected slots let you
1538
+ * arrange Logo / nav-links / actions however you like.
1539
+ *
1540
+ * ## Slots
1541
+ * - **`[toolbarStart]`** — Left-aligned content (logo, brand, hamburger)
1542
+ * - **Default `<ng-content>`** — Center / free-form content (nav links, search)
1543
+ * - **`[toolbarEnd]`** — Right-aligned content (user menu, theme toggle, actions)
1544
+ *
1545
+ * ## Layout
1546
+ * The toolbar uses `display:flex; align-items:center` with a spacer between
1547
+ * the default content and the end slot, so start items anchor left and end
1548
+ * items anchor right automatically.
1549
+ *
1550
+ * Usage:
1551
+ * ```html
1552
+ * <ng-oat-toolbar>
1553
+ * <a toolbarStart routerLink="/" class="brand">🌾 MyApp</a>
1554
+ * <nav>
1555
+ * <a routerLink="/home">Home</a>
1556
+ * <a routerLink="/about">About</a>
1557
+ * </nav>
1558
+ * <ng-oat-dropdown toolbarEnd>
1559
+ * <button trigger class="ghost">👤 User ▾</button>
1560
+ * <a role="menuitem">Profile</a>
1561
+ * <a role="menuitem">Settings</a>
1562
+ * <hr />
1563
+ * <a role="menuitem">Logout</a>
1564
+ * </ng-oat-dropdown>
1565
+ * </ng-oat-toolbar>
1566
+ * ```
1567
+ */
1568
+ declare class NgOatToolbar {
1569
+ readonly color: _angular_core.InputSignal<NgOatToolbarColor>;
1570
+ readonly dense: _angular_core.InputSignal<boolean>;
1571
+ readonly fixed: _angular_core.InputSignal<boolean>;
1572
+ protected attrVariant: _angular_core.Signal<"primary" | "accent" | null>;
1573
+ protected navClass: _angular_core.Signal<string>;
1574
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NgOatToolbar, never>;
1575
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NgOatToolbar, "ng-oat-toolbar", never, { "color": { "alias": "color"; "required": false; "isSignal": true; }; "dense": { "alias": "dense"; "required": false; "isSignal": true; }; "fixed": { "alias": "fixed"; "required": false; "isSignal": true; }; }, {}, never, ["[toolbarStart]", "*", "[toolbarEnd]"], true, never>;
1576
+ }
1577
+ /**
1578
+ * Toolbar row — use multiple rows stacked inside a toolbar.
1579
+ *
1580
+ * Usage:
1581
+ * ```html
1582
+ * <ng-oat-toolbar>
1583
+ * <ng-oat-toolbar-row>
1584
+ * <a toolbarStart>Brand</a>
1585
+ * <span toolbarEnd>Actions</span>
1586
+ * </ng-oat-toolbar-row>
1587
+ * <ng-oat-toolbar-row dense>
1588
+ * <nav>Sub-navigation tabs</nav>
1589
+ * </ng-oat-toolbar-row>
1590
+ * </ng-oat-toolbar>
1591
+ * ```
1592
+ */
1593
+ declare class NgOatToolbarRow {
1594
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NgOatToolbarRow, never>;
1595
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NgOatToolbarRow, "ng-oat-toolbar-row", never, {}, {}, never, ["*"], true, never>;
1596
+ }
1597
+
1598
+ type NgOatTheme = 'light' | 'dark' | 'system';
1599
+ /** Describes one option in the theme selector. */
1600
+ interface NgOatThemeOption {
1601
+ /** The theme value written to localStorage / emitted. */
1602
+ value: NgOatTheme;
1603
+ /** Human-readable label shown in dropdown items. */
1604
+ label: string;
1605
+ /** Optional text/emoji shown instead of the built-in SVG icon. */
1606
+ icon?: string;
1607
+ }
1608
+ /**
1609
+ * Structural directive for fully-custom icon rendering.
1610
+ *
1611
+ * Place inside `<ng-oat-theme-selector>` to replace the built-in SVG icons.
1612
+ * The template context receives the current `NgOatThemeOption` as `$implicit`.
1613
+ *
1614
+ * ```html
1615
+ * <ng-oat-theme-selector>
1616
+ * <ng-template ngOatThemeSelectorIcon let-opt>
1617
+ * <my-icon [name]="opt.value" />
1618
+ * </ng-template>
1619
+ * </ng-oat-theme-selector>
1620
+ * ```
1621
+ */
1622
+ declare class NgOatThemeSelectorIcon {
1623
+ readonly tpl: TemplateRef<any>;
1624
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NgOatThemeSelectorIcon, never>;
1625
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NgOatThemeSelectorIcon, "ng-template[ngOatThemeSelectorIcon]", never, {}, {}, never, never, true, never>;
1626
+ }
1627
+ /**
1628
+ * Theme selector — shadcn-inspired light / dark / system toggle.
1629
+ *
1630
+ * Applies `colorScheme` on `<html>` and persists the choice to `localStorage`.
1631
+ * When "system" is selected it respects `prefers-color-scheme`.
1632
+ *
1633
+ * Usage:
1634
+ * ```html
1635
+ * <!-- Dropdown-style (default) -->
1636
+ * <ng-oat-theme-selector />
1637
+ *
1638
+ * <!-- Inline toggle group style -->
1639
+ * <ng-oat-theme-selector mode="toggle" />
1640
+ *
1641
+ * <!-- Listen for changes -->
1642
+ * <ng-oat-theme-selector (themeChange)="onTheme($event)" />
1643
+ * ```
1644
+ */
1645
+ declare class NgOatThemeSelector implements OnInit {
1646
+ /** Display mode: dropdown menu or inline toggle group */
1647
+ readonly mode: _angular_core.InputSignal<"dropdown" | "toggle">;
1648
+ /** Initial theme (overrides localStorage if set) */
1649
+ readonly initialTheme: _angular_core.InputSignal<NgOatTheme | undefined>;
1650
+ /**
1651
+ * Custom theme options. Override labels, provide emoji icons, or
1652
+ * change the set entirely.
1653
+ *
1654
+ * ```html
1655
+ * <ng-oat-theme-selector
1656
+ * [themes]="[
1657
+ * { value: 'light', label: 'Day', icon: '🌅' },
1658
+ * { value: 'dark', label: 'Night', icon: '🌃' },
1659
+ * { value: 'system', label: 'Auto', icon: '🖥️' },
1660
+ * ]" />
1661
+ * ```
1662
+ */
1663
+ readonly themes: _angular_core.InputSignal<NgOatThemeOption[]>;
1664
+ /** Emits when the user picks a theme */
1665
+ readonly themeChange: _angular_core.OutputEmitterRef<NgOatTheme>;
1666
+ /** Current active theme */
1667
+ readonly current: _angular_core.WritableSignal<NgOatTheme>;
1668
+ readonly open: _angular_core.WritableSignal<boolean>;
1669
+ /** Content-projected custom icon template */
1670
+ protected readonly iconTpl: _angular_core.Signal<NgOatThemeSelectorIcon | undefined>;
1671
+ /** Resolved themes (input or defaults) */
1672
+ protected resolvedThemes: _angular_core.Signal<NgOatThemeOption[]>;
1673
+ /** The currently active option object */
1674
+ protected activeOption: _angular_core.Signal<NgOatThemeOption>;
1675
+ private doc;
1676
+ private platformId;
1677
+ private mediaQuery;
1678
+ private mediaListener;
1679
+ /** Toggle the dropdown open/closed */
1680
+ toggleOpen(): void;
1681
+ /** Pick a theme and close the dropdown */
1682
+ pick(theme: NgOatTheme): void;
1683
+ /** Keyboard handler for Escape and arrow-key navigation inside the menu */
1684
+ onHostKey(e: KeyboardEvent): void;
1685
+ ngOnInit(): void;
1686
+ setTheme(theme: NgOatTheme): void;
1687
+ private applyResolved;
1688
+ private onDocClick;
1689
+ /** @internal */
1690
+ ngOnDestroy(): void;
1691
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NgOatThemeSelector, never>;
1692
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NgOatThemeSelector, "ng-oat-theme-selector", never, { "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "initialTheme": { "alias": "initialTheme"; "required": false; "isSignal": true; }; "themes": { "alias": "themes"; "required": false; "isSignal": true; }; }, { "themeChange": "themeChange"; }, ["iconTpl"], never, true, never>;
1693
+ }
1694
+
1507
1695
  type NgOatInputType = 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search' | 'date' | 'datetime-local' | 'time' | 'month' | 'week' | 'color';
1508
1696
  /**
1509
1697
  * Oat-styled text input implementing `FormValueControl<string>`.
@@ -1732,5 +1920,5 @@ interface TooltipPositioner {
1732
1920
  /** DI token for tooltip positioning strategy */
1733
1921
  declare const TOOLTIP_POSITIONER: InjectionToken<TooltipPositioner>;
1734
1922
 
1735
- export { NG_OAT_CHIP_GROUP, NG_OAT_TOGGLE_GROUP, NgOatAccordion, NgOatAlert, NgOatAvatar, NgOatBadge, NgOatBreadcrumb, NgOatButton, NgOatCard, NgOatCardCarousel, NgOatCardFooter, NgOatCardHeader, NgOatCarousel, NgOatCheckbox, NgOatChip, NgOatChipGroup, NgOatChipInput, NgOatDialog, NgOatDialogComponent, NgOatDropdown, NgOatDropdownComponent, NgOatFileUpload, NgOatFormError, NgOatInput, NgOatInputOtp, NgOatMeter, NgOatPagination, NgOatProgress, NgOatRadioGroup, NgOatSearchInput, NgOatSelect, NgOatSeparator, NgOatSidebar, NgOatSidebarComponent, NgOatSkeleton, NgOatSpinner, NgOatSplitButton, NgOatSwitch, NgOatTable, NgOatTabs, NgOatTabsComponent, NgOatTextarea, NgOatThemeRef, NgOatToast, NgOatToggle, NgOatToggleGroup, NgOatTooltip, NgOatTooltipComponent, OAT_TOKEN_MAP, OAT_VERSION, OAT_VERSION_TOKEN, TOOLTIP_POSITIONER, provideNgOat, provideNgOatTheme };
1736
- export type { NgOatAccordionItem, NgOatAlertVariant, NgOatAvatarSize, NgOatBadgeVariant, NgOatButtonSize, NgOatButtonStyle, NgOatButtonVariant, NgOatCarouselAspectRatio, NgOatCarouselOrientation, NgOatCarouselSlide, NgOatChipGroupHost, NgOatChipSize, NgOatChipVariant, NgOatInputType, NgOatOptions, NgOatProductCard, NgOatRadioOption, NgOatSelectOption, NgOatSeparatorOrientation, NgOatSkeletonType, NgOatSpinnerSize, NgOatTabItem, NgOatTableColumn, NgOatThemeConfig, NgOatToastOptions, NgOatToastPlacement, NgOatToastVariant, NgOatToggleGroupHost, NgOatToggleGroupSize, NgOatToggleGroupVariant, NgOatToggleSize, NgOatToggleVariant, OatBreadcrumbItem, OatTokens, Placement, TooltipPositioner };
1923
+ export { NG_OAT_CHIP_GROUP, NG_OAT_TOGGLE_GROUP, NgOatAccordion, NgOatAlert, NgOatAvatar, NgOatBadge, NgOatBreadcrumb, NgOatButton, NgOatCard, NgOatCardCarousel, NgOatCardFooter, NgOatCardHeader, NgOatCarousel, NgOatCheckbox, NgOatChip, NgOatChipGroup, NgOatChipInput, NgOatDialog, NgOatDialogComponent, NgOatDropdown, NgOatDropdownComponent, NgOatFileUpload, NgOatFormError, NgOatInput, NgOatInputOtp, NgOatMeter, NgOatPagination, NgOatProgress, NgOatRadioGroup, NgOatSearchInput, NgOatSelect, NgOatSeparator, NgOatSidebar, NgOatSidebarComponent, NgOatSkeleton, NgOatSpinner, NgOatSplitButton, NgOatSwitch, NgOatTable, NgOatTabs, NgOatTabsComponent, NgOatTextarea, NgOatThemeRef, NgOatThemeSelector, NgOatThemeSelectorIcon, NgOatToast, NgOatToggle, NgOatToggleGroup, NgOatToolbar, NgOatToolbarRow, NgOatTooltip, NgOatTooltipComponent, OAT_TOKEN_MAP, OAT_VERSION, OAT_VERSION_TOKEN, TOOLTIP_POSITIONER, provideNgOat, provideNgOatTheme };
1924
+ export type { NgOatAccordionItem, NgOatAlertVariant, NgOatAvatarSize, NgOatBadgeVariant, NgOatButtonSize, NgOatButtonStyle, NgOatButtonVariant, NgOatCarouselAspectRatio, NgOatCarouselOrientation, NgOatCarouselSlide, NgOatChipGroupHost, NgOatChipSize, NgOatChipVariant, NgOatInputType, NgOatOptions, NgOatProductCard, NgOatRadioOption, NgOatSelectOption, NgOatSeparatorOrientation, NgOatSkeletonType, NgOatSpinnerSize, NgOatTabItem, NgOatTableColumn, NgOatTheme, NgOatThemeConfig, NgOatThemeOption, NgOatToastOptions, NgOatToastPlacement, NgOatToastVariant, NgOatToggleGroupHost, NgOatToggleGroupSize, NgOatToggleGroupVariant, NgOatToggleSize, NgOatToggleVariant, NgOatToolbarColor, OatBreadcrumbItem, OatTokens, Placement, TooltipPositioner };
@@ -1,107 +0,0 @@
1
- // oat - Base Web Component Class
2
- // Provides lifecycle management, event handling, and utilities.
3
-
4
- export class OtBase extends HTMLElement {
5
- #initialized = false;
6
-
7
- // Called when element is added to DOM.
8
- connectedCallback() {
9
- if (this.#initialized) return;
10
-
11
- // Wait for DOM to be ready.
12
- if (document.readyState === 'loading') {
13
- document.addEventListener('DOMContentLoaded', () => this.#setup(), { once: true });
14
- } else {
15
- this.#setup();
16
- }
17
- }
18
-
19
- // Private setup to ensure that init() is only called once.
20
- #setup() {
21
- if (this.#initialized) return;
22
- this.#initialized = true;
23
- this.init();
24
- }
25
-
26
- // Called when element is removed from DOM.
27
- disconnectedCallback() {
28
- this.cleanup();
29
- }
30
-
31
- // Override in subclass for cleanup logic.
32
- cleanup() {}
33
-
34
- // Central event handler - enables automatic cleanup.
35
- // Usage: element.addEventListener('click', this)
36
- handleEvent(event) {
37
- const handler = this[`on${event.type}`];
38
- if (handler) handler.call(this, event);
39
- }
40
-
41
- // Given a keyboard event (left, right, home, end), the current selection idx
42
- // total items in a list, return 0-n index of the next/previous item
43
- // for doing a roving keyboard nav.
44
- keyNav(event, idx, len, prevKey, nextKey, homeEnd = false) {
45
- const { key } = event;
46
- let next = -1;
47
-
48
- if (key === nextKey) {
49
- next = (idx + 1) % len;
50
- } else if (key === prevKey) {
51
- next = (idx - 1 + len) % len;
52
- } else if (homeEnd) {
53
- if (key === 'Home') {
54
- next = 0;
55
- } else if (key === 'End') {
56
- next = len - 1;
57
- }
58
- }
59
-
60
- if (next >= 0) event.preventDefault();
61
- return next;
62
- }
63
-
64
- // Emit a custom event.
65
- emit(name, detail = null) {
66
- return this.dispatchEvent(new CustomEvent(name, {
67
- bubbles: true,
68
- composed: true,
69
- cancelable: true,
70
- detail
71
- }));
72
- }
73
-
74
- // Query selector within this element.
75
- $(selector) {
76
- return this.querySelector(selector);
77
- }
78
-
79
- // Query selector all within this element.
80
- $$(selector) {
81
- return Array.from(this.querySelectorAll(selector));
82
- }
83
-
84
- // Generate a unique ID string.
85
- uid() {
86
- return Math.random().toString(36).slice(2, 10);
87
- }
88
- }
89
-
90
- // Polyfill for command/commandfor (Safari)
91
- if (!('commandForElement' in HTMLButtonElement.prototype)) {
92
- document.addEventListener('click', e => {
93
- const btn = e.target.closest('button[commandfor]');
94
- if (!btn) return;
95
-
96
- const target = document.getElementById(btn.getAttribute('commandfor'));
97
- if (!target) return;
98
-
99
- const command = btn.getAttribute('command') || 'toggle';
100
-
101
- if (target instanceof HTMLDialogElement) {
102
- if (command === 'show-modal') target.showModal();
103
- else if (command === 'close') target.close();
104
- else target.open ? target.close() : target.showModal();
105
- }
106
- });
107
- }
@@ -1,74 +0,0 @@
1
- /**
2
- * oat - Dropdown Component
3
- * Provides positioning, keyboard navigation, and ARIA state management.
4
- *
5
- * Usage:
6
- * <ot-dropdown>
7
- * <button popovertarget="menu-id">Options</button>
8
- * <menu popover id="menu-id">
9
- * <button role="menuitem">Item 1</button>
10
- * <button role="menuitem">Item 2</button>
11
- * </menu>
12
- * </ot-dropdown>
13
- */
14
-
15
- import { OtBase } from './base.js';
16
-
17
- class OtDropdown extends OtBase {
18
- #menu;
19
- #trigger;
20
- #position;
21
- #items;
22
-
23
- init() {
24
- this.#menu = this.$('[popover]');
25
- this.#trigger = this.$('[popovertarget]');
26
-
27
- if (!this.#menu || !this.#trigger) return;
28
-
29
- this.#menu.addEventListener('toggle', this);
30
- this.#menu.addEventListener('keydown', this);
31
-
32
- this.#position = () => {
33
- // Position has to be calculated and applied manually because
34
- // popover positioning is like fixed, relative to the window.
35
- const r = this.#trigger.getBoundingClientRect();
36
- const m = this.#menu.getBoundingClientRect();
37
-
38
- // Flip if menu overflows viewport.
39
- this.#menu.style.top = `${r.bottom + m.height > window.innerHeight ? r.top - m.height : r.bottom}px`;
40
- this.#menu.style.left = `${r.left + m.width > window.innerWidth ? r.right - m.width : r.left}px`;
41
- };
42
- }
43
-
44
- ontoggle(e) {
45
- if (e.newState === 'open') {
46
- this.#position();
47
- window.addEventListener('scroll', this.#position, true);
48
- window.addEventListener('resize', this.#position);
49
- this.#items = this.$$('[role="menuitem"]');
50
- this.#items[0]?.focus();
51
- this.#trigger.ariaExpanded = 'true';
52
- } else {
53
- this.cleanup();
54
- this.#items = null;
55
- this.#trigger.ariaExpanded = 'false';
56
- this.#trigger.focus();
57
- }
58
- }
59
-
60
- onkeydown(e) {
61
- if (!e.target.matches('[role="menuitem"]')) return;
62
-
63
- const idx = this.#items.indexOf(e.target);
64
- const next = this.keyNav(e, idx, this.#items.length, 'ArrowUp', 'ArrowDown', true);
65
- if (next >= 0) this.#items[next].focus();
66
- }
67
-
68
- cleanup() {
69
- window.removeEventListener('scroll', this.#position, true);
70
- window.removeEventListener('resize', this.#position);
71
- }
72
- }
73
-
74
- customElements.define('ot-dropdown', OtDropdown);
@@ -1,12 +0,0 @@
1
- import './base.js';
2
- import './tabs.js';
3
- import './dropdown.js';
4
- import './tooltip.js';
5
- import './sidebar.js';
6
- import { toast, toastEl, toastClear } from './toast.js';
7
-
8
- // Register the global window.ot.* APIs.
9
- const ot = window.ot || (window.ot = {});
10
- ot.toast = toast;
11
- ot.toast.el = toastEl;
12
- ot.toast.clear = toastClear;
@@ -1,22 +0,0 @@
1
- /**
2
- * Sidebar toggle handler
3
- * Toggles data-sidebar-open on layout when toggle button is clicked
4
- */
5
- document.addEventListener('click', (e) => {
6
- const toggle = e.target.closest('[data-sidebar-toggle]');
7
- if (toggle) {
8
- const layout = toggle.closest('[data-sidebar-layout]');
9
- layout?.toggleAttribute('data-sidebar-open');
10
- return;
11
- }
12
-
13
- // Dismiss sidebar when clicking outside (when sidebar is not an overlay).
14
- if (!e.target.closest('[data-sidebar]')) {
15
- const layout = document.querySelector('[data-sidebar-layout][data-sidebar-open]');
16
- // Hardcode breakpoint (for now) as there's no way to use a CSS variable in
17
- // the @media{} query which could've been picked up here.
18
- if (layout && window.matchMedia('(max-width: 768px)').matches) {
19
- layout.removeAttribute('data-sidebar-open');
20
- }
21
- }
22
- });
@@ -1,94 +0,0 @@
1
- /**
2
- * oat - Tabs Component
3
- * Provides keyboard navigation and ARIA state management.
4
- *
5
- * Usage:
6
- * <ot-tabs>
7
- * <div role="tablist">
8
- * <button role="tab">Tab 1</button>
9
- * <button role="tab">Tab 2</button>
10
- * </div>
11
- * <div role="tabpanel">Content 1</div>
12
- * <div role="tabpanel">Content 2</div>
13
- * </ot-tabs>
14
- */
15
-
16
- import { OtBase } from './base.js';
17
-
18
- class OtTabs extends OtBase {
19
- #tabs = [];
20
- #panels = [];
21
-
22
- init() {
23
- const tablist = this.$(':scope > [role="tablist"]');
24
- this.#tabs = tablist ? [...tablist.querySelectorAll('[role="tab"]')] : [];
25
- this.#panels = this.$$(':scope > [role="tabpanel"]');
26
-
27
- if (this.#tabs.length === 0 || this.#panels.length === 0) {
28
- console.warn('ot-tabs: Missing tab or tabpanel elements');
29
- return;
30
- }
31
-
32
- // Generate IDs and set up ARIA.
33
- this.#tabs.forEach((tab, i) => {
34
- const panel = this.#panels[i];
35
- if (!panel) return;
36
-
37
- const tabId = tab.id || `ot-tab-${this.uid()}`;
38
- const panelId = panel.id || `ot-panel-${this.uid()}`;
39
-
40
- tab.id = tabId;
41
- panel.id = panelId;
42
- tab.setAttribute('aria-controls', panelId);
43
- panel.setAttribute('aria-labelledby', tabId);
44
- });
45
-
46
- tablist.addEventListener('click', this);
47
- tablist.addEventListener('keydown', this);
48
-
49
- // Find initially active tab or default to first.
50
- const activeTab = this.#tabs.findIndex(t => t.ariaSelected === 'true');
51
- this.#activate(activeTab >= 0 ? activeTab : 0);
52
- }
53
-
54
- onclick(e) {
55
- const index = this.#tabs.indexOf(e.target.closest('[role="tab"]'));
56
- if (index >= 0) this.#activate(index);
57
- }
58
-
59
- onkeydown(e) {
60
- if (!e.target.closest('[role="tab"]')) return;
61
-
62
- const next = this.keyNav(e, this.activeIndex, this.#tabs.length, 'ArrowLeft', 'ArrowRight');
63
- if (next >= 0) {
64
- this.#activate(next);
65
- this.#tabs[next].focus();
66
- }
67
- }
68
-
69
- #activate(idx) {
70
- this.#tabs.forEach((tab, i) => {
71
- const isActive = i === idx;
72
- tab.ariaSelected = String(isActive);
73
- tab.tabIndex = isActive ? 0 : -1;
74
- });
75
-
76
- this.#panels.forEach((panel, i) => {
77
- panel.hidden = i !== idx;
78
- });
79
-
80
- this.emit('ot-tab-change', { index: idx, tab: this.#tabs[idx] });
81
- }
82
-
83
- get activeIndex() {
84
- return this.#tabs.findIndex(t => t.ariaSelected === 'true');
85
- }
86
-
87
- set activeIndex(value) {
88
- if (value >= 0 && value < this.#tabs.length) {
89
- this.#activate(value);
90
- }
91
- }
92
- }
93
-
94
- customElements.define('ot-tabs', OtTabs);