@frame-kit/ui-ng-patterns 0.2.12 → 0.2.14

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": "@frame-kit/ui-ng-patterns",
3
- "version": "0.2.12",
3
+ "version": "0.2.14",
4
4
  "description": "Opinionated, dashboard-ready Angular component patterns composed from @frame-kit/ui-ng.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -17,7 +17,7 @@
17
17
  "@angular/forms": "^21.2.0",
18
18
  "@angular/platform-browser": "^21.2.0",
19
19
  "@angular/router": "^21.2.0",
20
- "@frame-kit/ui-ng": "0.2.12",
20
+ "@frame-kit/ui-ng": "0.2.14",
21
21
  "rxjs": "^7.0.0"
22
22
  },
23
23
  "sideEffects": false,
@@ -2,6 +2,7 @@ import * as _angular_core from '@angular/core';
2
2
  import { TemplateRef } from '@angular/core';
3
3
  import { HeadlineLevel } from '@frame-kit/ui-ng/core/headline';
4
4
  import * as i1 from '@angular/cdk/drag-drop';
5
+ import { AppShellMode } from '@frame-kit/ui-ng/layouts/app-shell';
5
6
 
6
7
  /**
7
8
  * Page-level dashboard header: title + optional description on the left, up to
@@ -1231,5 +1232,287 @@ declare class ContentDetailPageComponent {
1231
1232
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<ContentDetailPageComponent, "fk-content-detail-page", never, { "heading": { "alias": "heading"; "required": false; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "backLabel": { "alias": "backLabel"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "back": "back"; }, never, ["[backIcon]", "[headline]", "[subtitle]", "[actions]", "*"], true, never>;
1232
1233
  }
1233
1234
 
1234
- export { BackNavComponent, ColumnsComponent, ContentColumnComponent, ContentColumnHeadlineComponent, ContentDetailHeaderComponent, ContentDetailPageComponent, ContentPageSectionComponent, ContentRowComponent, ContentSplitLayoutComponent, DetailPanelComponent, DialogFooterComponent, DialogHeaderComponent, DialogShellComponent, OnboardingGuideComponent, PageHeaderComponent, SearchBarLayoutComponent, SidebarNavComponent, SidebarNavItemDirective, TableEmptyStateComponent };
1235
- export type { ColumnsEmptyTracks, DialogConfirmVariant, DialogHeaderTint, OnboardingStep, OnboardingSubstep, OnboardingSubstepToggle, SidebarNavChild, SidebarNavGroup, SidebarNavItem, SidebarNavLink, SidebarNavModel };
1235
+ /**
1236
+ * The line a region shows while it fetches something of its own: a small
1237
+ * spinner beside a label, centered where the content will be.
1238
+ *
1239
+ * Sits in place of the content so a section that is still resolving reads as
1240
+ * working rather than as empty. Mono and faint because it is machinery
1241
+ * reporting itself, not content that has arrived.
1242
+ *
1243
+ * Not for a whole page — a page has its own loading state. This is for a
1244
+ * section whose data comes separately from the rest: a drawer panel, a
1245
+ * capability list, an audit page inside a detail view.
1246
+ *
1247
+ * <fk-inline-loader label="Loading capabilities…" />
1248
+ *
1249
+ * The label doubles as the spinner's accessible name — one string, so the two
1250
+ * cannot drift.
1251
+ */
1252
+ declare class InlineLoaderComponent {
1253
+ /** What is being fetched, already translated. Also the spinner's aria-label. */
1254
+ readonly label: _angular_core.InputSignal<string>;
1255
+ readonly className: _angular_core.InputSignal<string>;
1256
+ readonly id: _angular_core.InputSignal<string | null>;
1257
+ readonly classes: _angular_core.Signal<string>;
1258
+ get hostClass(): string;
1259
+ get hostId(): string | null;
1260
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<InlineLoaderComponent, never>;
1261
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<InlineLoaderComponent, "fk-inline-loader", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1262
+ }
1263
+
1264
+ /** One selectable row in a column. */
1265
+ interface ContextSwitcherOption {
1266
+ id: string;
1267
+ label: string;
1268
+ }
1269
+ /**
1270
+ * A titled list of options. Most switchers have one; a two-column switcher
1271
+ * (say, Applications beside Environments) is the only structural variation.
1272
+ */
1273
+ interface ContextSwitcherColumn {
1274
+ /** Identifies the column in the `select` event — the parent acts on it. */
1275
+ key: string;
1276
+ title: string;
1277
+ options: readonly ContextSwitcherOption[];
1278
+ /** The row to paint as current, if any. */
1279
+ activeId: string | null;
1280
+ }
1281
+ /** Which row was chosen, and which column it came from. */
1282
+ interface ContextSwitcherSelection {
1283
+ columnKey: string;
1284
+ option: ContextSwitcherOption;
1285
+ }
1286
+ /** Badge treatment: tinted (default) or solid, for the darker of two stacked controls. */
1287
+ type ContextSwitcherIconTone = 'brand' | 'solid';
1288
+ /** Sub-label treatment: a static category, or live context worth reading at a glance. */
1289
+ type ContextSwitcherSublabelTone = 'muted' | 'primary';
1290
+
1291
+ /**
1292
+ * The sidebar control for "which context am I working in": a trigger showing
1293
+ * the current name over a small category line, and a panel of one or more
1294
+ * titled option columns.
1295
+ *
1296
+ * Several of these stack in a rail (an account, an application, an
1297
+ * environment), and any visible difference between them reads as a mistake
1298
+ * rather than a distinction — so the chrome lives here once, and the meaning
1299
+ * stays with the caller. This holds no data, fetches nothing, and knows
1300
+ * nothing about what the options are; it reports what was clicked.
1301
+ *
1302
+ * <fk-context-switcher
1303
+ * [name]="accountName()"
1304
+ * sublabel="Account"
1305
+ * ariaLabel="Switch account"
1306
+ * [columns]="columns()"
1307
+ * (optionSelect)="onSelect($event)"
1308
+ * >
1309
+ * <fk-icon contextSwitcherIcon name="organization" />
1310
+ * </fk-context-switcher>
1311
+ *
1312
+ * Closing is the caller's call: choosing an account commits and should close,
1313
+ * while choosing an application in a two-column switcher only re-focuses the
1314
+ * second column and must stay open. Call `close()` when you mean to.
1315
+ */
1316
+ declare class ContextSwitcherComponent {
1317
+ /**
1318
+ * The trigger's headline — the context you are in. Null renders a
1319
+ * skeleton rather than an empty row, for a first load with nothing cached.
1320
+ */
1321
+ readonly name: _angular_core.InputSignal<string | null>;
1322
+ /**
1323
+ * The small mono line beneath the name — the kind of thing the name is
1324
+ * ("Account"), or a second piece of live context. Null renders a skeleton;
1325
+ * empty renders nothing.
1326
+ */
1327
+ readonly sublabel: _angular_core.InputSignal<string | null>;
1328
+ readonly iconTone: _angular_core.InputSignal<ContextSwitcherIconTone>;
1329
+ readonly sublabelTone: _angular_core.InputSignal<ContextSwitcherSublabelTone>;
1330
+ /** Icon-rail mode — the trigger collapses to its badge. */
1331
+ readonly collapsed: _angular_core.InputSignal<boolean>;
1332
+ /**
1333
+ * The collapsed trigger shows no text, so it carries the current context in
1334
+ * its label instead. Falls back to `ariaLabel`.
1335
+ */
1336
+ readonly collapsedAriaLabel: _angular_core.InputSignal<string>;
1337
+ /** One column, or several. Empty columns render nothing. */
1338
+ readonly columns: _angular_core.InputSignal<readonly ContextSwitcherColumn[]>;
1339
+ readonly optionSelect: _angular_core.OutputEmitterRef<ContextSwitcherSelection>;
1340
+ readonly className: _angular_core.InputSignal<string>;
1341
+ readonly id: _angular_core.InputSignal<string | null>;
1342
+ /** Describes the control itself. */
1343
+ readonly ariaLabel: _angular_core.InputSignal<string>;
1344
+ private readonly dropdown;
1345
+ /** Two columns sit side by side; one fills the panel. */
1346
+ readonly multiColumn: _angular_core.Signal<boolean>;
1347
+ readonly triggerAriaLabel: _angular_core.Signal<string>;
1348
+ readonly classes: _angular_core.Signal<string>;
1349
+ get hostClass(): string;
1350
+ get hostId(): string | null;
1351
+ /** Close the panel. The caller decides when a selection is final. */
1352
+ close(): void;
1353
+ protected onSelect(column: ContextSwitcherColumn, option: ContextSwitcherOption): void;
1354
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ContextSwitcherComponent, never>;
1355
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ContextSwitcherComponent, "fk-context-switcher", never, { "name": { "alias": "name"; "required": false; "isSignal": true; }; "sublabel": { "alias": "sublabel"; "required": false; "isSignal": true; }; "iconTone": { "alias": "iconTone"; "required": false; "isSignal": true; }; "sublabelTone": { "alias": "sublabelTone"; "required": false; "isSignal": true; }; "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; "collapsedAriaLabel": { "alias": "collapsedAriaLabel"; "required": false; "isSignal": true; }; "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "optionSelect": "optionSelect"; }, never, ["[contextSwitcherIcon]"], true, never>;
1356
+ }
1357
+
1358
+ /** A child destination that may be hidden by the caller's authority. */
1359
+ type DashboardNavChild = SidebarNavChild & {
1360
+ /** `false` drops the row. Absent counts as visible. */
1361
+ visible?: boolean;
1362
+ };
1363
+ /** A top-level destination, or a parent whose children may be hidden. */
1364
+ type DashboardNavItem = Omit<SidebarNavItem, 'children'> & {
1365
+ visible?: boolean;
1366
+ children?: DashboardNavChild[];
1367
+ };
1368
+ interface DashboardNavGroup {
1369
+ /** Stable identity, used for tracking and for anchoring the projected slots. */
1370
+ id: string;
1371
+ /** Section heading; rendered by `fk-sidebar-nav`. Omit for an unlabelled group. */
1372
+ label?: string;
1373
+ items: DashboardNavItem[];
1374
+ /**
1375
+ * Draw a hairline above this group. For a group whose neighbours change
1376
+ * with capabilities, the gap alone reads as a boundary only while the
1377
+ * earlier group is tall — the line keeps it legible in every state.
1378
+ * Suppressed when the group renders first.
1379
+ */
1380
+ separatorBefore?: boolean;
1381
+ }
1382
+
1383
+ interface NavSegment {
1384
+ id: string;
1385
+ before: boolean;
1386
+ after: boolean;
1387
+ separated: boolean;
1388
+ model: SidebarNavModel;
1389
+ }
1390
+ /**
1391
+ * A dashboard's sidebar navigation: the caller's groups, with items the
1392
+ * caller may not open already removed, rendered as one `fk-sidebar-nav` per
1393
+ * group so that projected controls — an account switcher, an environment
1394
+ * switcher — can sit between groups.
1395
+ *
1396
+ * <fk-dashboard-nav
1397
+ * [groups]="groups()"
1398
+ * [collapsed]="collapsed()"
1399
+ * slotBeforeGroupId="workspace"
1400
+ * slotAfterGroupId="access"
1401
+ * >
1402
+ * <div navSlotBefore><fk-context-switcher … /></div>
1403
+ * <div><fk-context-switcher … /></div>
1404
+ * </fk-dashboard-nav>
1405
+ *
1406
+ * Visibility is a plain boolean on each item, decided by the caller from
1407
+ * whatever authority it has; this component only removes what is hidden. A
1408
+ * parent whose children have all been removed goes too — it would expand to
1409
+ * nothing. Disclosure, active state and the collapsed-rail flyout are owned
1410
+ * by `fk-sidebar-nav`.
1411
+ */
1412
+ declare class DashboardNavComponent {
1413
+ readonly groups: _angular_core.InputSignal<DashboardNavGroup[]>;
1414
+ /** Icon-rail mode, passed through to every segment. */
1415
+ readonly collapsed: _angular_core.InputSignal<boolean>;
1416
+ /** Project `[navSlotBefore]` content immediately before this group. */
1417
+ readonly slotBeforeGroupId: _angular_core.InputSignal<string | null>;
1418
+ /** Project default `<ng-content>` immediately after this group. */
1419
+ readonly slotAfterGroupId: _angular_core.InputSignal<string | null>;
1420
+ readonly className: _angular_core.InputSignal<string>;
1421
+ readonly id: _angular_core.InputSignal<string | null>;
1422
+ readonly ariaLabel: _angular_core.InputSignal<string | null>;
1423
+ /**
1424
+ * Visible groups sliced into render segments, each carrying a single-group
1425
+ * `SidebarNavModel`.
1426
+ */
1427
+ private readonly segments;
1428
+ /**
1429
+ * Groups above the projected `[navSlotBefore]` content, and those below it.
1430
+ * Split rather than conditionally projecting inside the loop: the slot's
1431
+ * position is expressed by what is emitted around it, so the template needs
1432
+ * exactly one outlet — Angular projects to the first matching `ng-content`,
1433
+ * and a second copy (or one inside a false `@if`) swallows the content.
1434
+ */
1435
+ readonly segmentsBeforeSlot: _angular_core.Signal<NavSegment[]>;
1436
+ readonly segmentsAfterSlot: _angular_core.Signal<NavSegment[]>;
1437
+ readonly classes: _angular_core.Signal<string>;
1438
+ get hostClass(): string;
1439
+ get hostId(): string | null;
1440
+ get hostAriaLabel(): string | null;
1441
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DashboardNavComponent, never>;
1442
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DashboardNavComponent, "fk-dashboard-nav", never, { "groups": { "alias": "groups"; "required": true; "isSignal": true; }; "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; "slotBeforeGroupId": { "alias": "slotBeforeGroupId"; "required": false; "isSignal": true; }; "slotAfterGroupId": { "alias": "slotAfterGroupId"; "required": false; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, {}, never, ["[navSlotBefore]", "*"], true, never>;
1443
+ }
1444
+
1445
+ /**
1446
+ * The chrome of a signed-in dashboard, built on `fk-app-shell`: a full-height
1447
+ * aside with a brand header and collapse toggle, a sticky frosted topbar with
1448
+ * a mobile hamburger, a capped page area with an optional loading veil, and a
1449
+ * footer. Every dashboard that uses it gets the identical structure — the
1450
+ * same hamburger, the same collapse, the same scroll behaviour — and differs
1451
+ * only in what it projects.
1452
+ *
1453
+ * <fk-dashboard-layout [pageLoading]="navigating()">
1454
+ * <fk-nav-brand layoutBrand brandText="Acme" />
1455
+ * <fk-dashboard-nav layoutAside [groups]="groups()" [collapsed]="collapsed()" />
1456
+ * <nav layoutTopbarStart>Acme / Overview</nav>
1457
+ * <fk-user-menu layoutTopbarEnd … />
1458
+ * <router-outlet />
1459
+ * <span layoutFooter>© Acme</span>
1460
+ * </fk-dashboard-layout>
1461
+ *
1462
+ * A dashboard that needs a trailing panel — a documentation reader, a detail
1463
+ * inspector — projects it into `[layoutEnd]` and chooses its `endMode`. One
1464
+ * that needs a panel beside the page, such as a secondary navigation, projects
1465
+ * it into `[layoutContentPanel]`, which sits in the content row ahead of the
1466
+ * scroll container so the page scrolls under it rather than with it.
1467
+ *
1468
+ * The aside's toggle and the topbar's hamburger drive the underlying shell;
1469
+ * `collapsed` reports the rail state so projected controls can follow it.
1470
+ * The toggle glyphs are projection slots with a built-in fallback, so no icon
1471
+ * registry is required.
1472
+ */
1473
+ declare class DashboardLayoutComponent {
1474
+ /** Passed to `fk-app-shell`. `icon` collapses to a rail; `side` hides entirely. */
1475
+ readonly mode: _angular_core.InputSignal<AppShellMode>;
1476
+ readonly sidenavWidth: _angular_core.InputSignal<string>;
1477
+ readonly collapsedWidth: _angular_core.InputSignal<string>;
1478
+ /** Layout mode for the trailing panel, when `[layoutEnd]` is projected. */
1479
+ readonly endMode: _angular_core.InputSignal<AppShellMode>;
1480
+ readonly endWidth: _angular_core.InputSignal<string>;
1481
+ readonly endCollapsedWidth: _angular_core.InputSignal<string>;
1482
+ /** Veil the page area with the projected `[layoutPageLoader]` content. */
1483
+ readonly pageLoading: _angular_core.InputSignal<boolean>;
1484
+ /** Accessible name for the aside's collapse toggle. */
1485
+ readonly toggleAriaLabel: _angular_core.InputSignal<string>;
1486
+ /** Accessible name for the topbar's mobile hamburger. */
1487
+ readonly menuAriaLabel: _angular_core.InputSignal<string>;
1488
+ readonly className: _angular_core.InputSignal<string>;
1489
+ readonly id: _angular_core.InputSignal<string | null>;
1490
+ private readonly shell;
1491
+ /** Whether the aside is in its icon rail. Projected controls follow this. */
1492
+ readonly collapsed: _angular_core.Signal<boolean>;
1493
+ /** Whether the trailing panel is showing. Projected controls follow this. */
1494
+ readonly endOpen: _angular_core.Signal<boolean>;
1495
+ readonly classes: _angular_core.Signal<string>;
1496
+ get hostClass(): string;
1497
+ get hostId(): string | null;
1498
+ /** The aside header's toggle: collapse or expand the rail. */
1499
+ toggleSidenav(): void;
1500
+ /** The topbar hamburger: open the off-canvas aside on narrow viewports. */
1501
+ openSidenav(): void;
1502
+ /**
1503
+ * Collapse the rail from outside the header's own toggle — what a dashboard
1504
+ * calls when it opens the trailing panel and wants the room.
1505
+ */
1506
+ closeSidenav(): void;
1507
+ /** Show the projected `[layoutEnd]` panel. */
1508
+ openEnd(): void;
1509
+ /** Hide it. */
1510
+ closeEnd(): void;
1511
+ /** Hide it if it is showing, show it if it is not. */
1512
+ toggleEnd(): void;
1513
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DashboardLayoutComponent, never>;
1514
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DashboardLayoutComponent, "fk-dashboard-layout", never, { "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "sidenavWidth": { "alias": "sidenavWidth"; "required": false; "isSignal": true; }; "collapsedWidth": { "alias": "collapsedWidth"; "required": false; "isSignal": true; }; "endMode": { "alias": "endMode"; "required": false; "isSignal": true; }; "endWidth": { "alias": "endWidth"; "required": false; "isSignal": true; }; "endCollapsedWidth": { "alias": "endCollapsedWidth"; "required": false; "isSignal": true; }; "pageLoading": { "alias": "pageLoading"; "required": false; "isSignal": true; }; "toggleAriaLabel": { "alias": "toggleAriaLabel"; "required": false; "isSignal": true; }; "menuAriaLabel": { "alias": "menuAriaLabel"; "required": false; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, {}, never, ["[layoutBanner]", "[layoutBrand]", "[layoutToggleIcon]", "[layoutAside]", "[layoutAsideFooter]", "[layoutContentPanel]", "[layoutMenuIcon]", "[layoutTopbarStart]", "[layoutTopbarEnd]", "*", "[layoutPageLoader]", "[layoutEnd]", "[layoutFooter]"], true, never>;
1515
+ }
1516
+
1517
+ export { BackNavComponent, ColumnsComponent, ContentColumnComponent, ContentColumnHeadlineComponent, ContentDetailHeaderComponent, ContentDetailPageComponent, ContentPageSectionComponent, ContentRowComponent, ContentSplitLayoutComponent, ContextSwitcherComponent, DashboardLayoutComponent, DashboardNavComponent, DetailPanelComponent, DialogFooterComponent, DialogHeaderComponent, DialogShellComponent, InlineLoaderComponent, OnboardingGuideComponent, PageHeaderComponent, SearchBarLayoutComponent, SidebarNavComponent, SidebarNavItemDirective, TableEmptyStateComponent };
1518
+ export type { ColumnsEmptyTracks, ContextSwitcherColumn, ContextSwitcherIconTone, ContextSwitcherOption, ContextSwitcherSelection, ContextSwitcherSublabelTone, DashboardNavChild, DashboardNavGroup, DashboardNavItem, DialogConfirmVariant, DialogHeaderTint, OnboardingStep, OnboardingSubstep, OnboardingSubstepToggle, SidebarNavChild, SidebarNavGroup, SidebarNavItem, SidebarNavLink, SidebarNavModel };