@frame-kit/ui-ng 0.1.6 → 0.1.7

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,27 +1,43 @@
1
- import * as _angular_core from '@angular/core';
2
- import { ElementRef } from '@angular/core';
1
+ import * as i0 from '@angular/core';
2
+ import { ElementRef, InjectionToken } from '@angular/core';
3
3
 
4
4
  declare class MenuItemComponent {
5
- /** Optional icon name rendered to the leading side of the label. */
6
- readonly icon: _angular_core.InputSignal<string | null>;
7
5
  /** When true, applies destructive (danger) color styling to the item. */
8
- readonly danger: _angular_core.InputSignal<boolean>;
6
+ readonly danger: i0.InputSignal<boolean>;
9
7
  /** When true, prevents interaction and applies disabled styling. */
10
- readonly disabled: _angular_core.InputSignal<boolean>;
8
+ readonly disabled: i0.InputSignal<boolean>;
11
9
  /** Fires when the user activates the item via click or keyboard. */
12
- readonly itemClick: _angular_core.OutputEmitterRef<void>;
10
+ readonly itemClick: i0.OutputEmitterRef<void>;
13
11
  readonly elRef: ElementRef<any>;
14
- readonly classes: _angular_core.Signal<string>;
12
+ readonly classes: i0.Signal<string>;
15
13
  get hostClass(): string;
16
14
  onClick(): void;
17
15
  onKeydown(event: KeyboardEvent): void;
18
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<MenuItemComponent, never>;
19
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<MenuItemComponent, "fk-menu-item", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "danger": { "alias": "danger"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "itemClick": "itemClick"; }, never, ["*"], true, never>;
16
+ static ɵfac: i0.ɵɵFactoryDeclaration<MenuItemComponent, never>;
17
+ static ɵcmp: i0.ɵɵComponentDeclaration<MenuItemComponent, "fk-menu-item", never, { "danger": { "alias": "danger"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "itemClick": "itemClick"; }, never, ["[fkMenuItemIcon]", "*"], true, never>;
18
+ }
19
+
20
+ /** Injection token for the leading-icon slot of `fk-menu-item`. */
21
+ declare const FK_MENU_ITEM_ICON: InjectionToken<FkMenuItemIconDirective>;
22
+ /**
23
+ * Marks projected content as the leading icon of an `fk-menu-item`. Project any
24
+ * icon element (the app owns the icon system):
25
+ *
26
+ * ```html
27
+ * <fk-menu-item>
28
+ * <fk-icon fkMenuItemIcon name="pen-outline" />
29
+ * Edit
30
+ * </fk-menu-item>
31
+ * ```
32
+ */
33
+ declare class FkMenuItemIconDirective {
34
+ static ɵfac: i0.ɵɵFactoryDeclaration<FkMenuItemIconDirective, never>;
35
+ static ɵdir: i0.ɵɵDirectiveDeclaration<FkMenuItemIconDirective, "[fkMenuItemIcon]", never, {}, {}, never, never, true, never>;
20
36
  }
21
37
 
22
38
  declare class MenuSeparatorComponent {
23
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<MenuSeparatorComponent, never>;
24
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<MenuSeparatorComponent, "fk-menu-separator", never, {}, {}, never, never, true, never>;
39
+ static ɵfac: i0.ɵɵFactoryDeclaration<MenuSeparatorComponent, never>;
40
+ static ɵcmp: i0.ɵɵComponentDeclaration<MenuSeparatorComponent, "fk-menu-separator", never, {}, {}, never, never, true, never>;
25
41
  }
26
42
 
27
- export { MenuItemComponent, MenuSeparatorComponent };
43
+ export { FK_MENU_ITEM_ICON, FkMenuItemIconDirective, MenuItemComponent, MenuSeparatorComponent };
@@ -55,6 +55,11 @@ declare class FkNodeTreeComponent {
55
55
  readonly selectedNodeId: _angular_core.InputSignal<string | undefined>;
56
56
  /** Controlled set of expanded node IDs; when undefined the tree manages expansion internally. */
57
57
  readonly expandedNodeIds: _angular_core.InputSignal<string[] | undefined>;
58
+ /** Optional template rendering each row's leading icon (the app owns the icon
59
+ * system). Receives the node as `$implicit`; render it from `node.icon`. */
60
+ readonly iconTemplate: _angular_core.InputSignal<TemplateRef<{
61
+ $implicit: FlatNode;
62
+ }> | undefined>;
58
63
  /** When true, rows can be dragged to reorder or reparent nodes. */
59
64
  readonly draggable: _angular_core.InputSignal<boolean>;
60
65
  /** Optional guard called during drag to determine whether a drop at a given position is allowed. */
@@ -90,7 +95,7 @@ declare class FkNodeTreeComponent {
90
95
  private clearAutoExpand;
91
96
  private resetDragState;
92
97
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<FkNodeTreeComponent, never>;
93
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<FkNodeTreeComponent, "fk-node-tree", never, { "className": { "alias": "className"; "required": false; "isSignal": true; }; "nodes": { "alias": "nodes"; "required": true; "isSignal": true; }; "selectedNodeId": { "alias": "selectedNodeId"; "required": false; "isSignal": true; }; "expandedNodeIds": { "alias": "expandedNodeIds"; "required": false; "isSignal": true; }; "draggable": { "alias": "draggable"; "required": false; "isSignal": true; }; "canDrop": { "alias": "canDrop"; "required": false; "isSignal": true; }; }, { "nodeSelect": "nodeSelect"; "nodeExpand": "nodeExpand"; "nodeDrop": "nodeDrop"; }, ["rowTemplate"], never, true, never>;
98
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<FkNodeTreeComponent, "fk-node-tree", never, { "className": { "alias": "className"; "required": false; "isSignal": true; }; "nodes": { "alias": "nodes"; "required": true; "isSignal": true; }; "selectedNodeId": { "alias": "selectedNodeId"; "required": false; "isSignal": true; }; "expandedNodeIds": { "alias": "expandedNodeIds"; "required": false; "isSignal": true; }; "iconTemplate": { "alias": "iconTemplate"; "required": false; "isSignal": true; }; "draggable": { "alias": "draggable"; "required": false; "isSignal": true; }; "canDrop": { "alias": "canDrop"; "required": false; "isSignal": true; }; }, { "nodeSelect": "nodeSelect"; "nodeExpand": "nodeExpand"; "nodeDrop": "nodeDrop"; }, ["rowTemplate"], never, true, never>;
94
99
  }
95
100
 
96
101
  declare function isDescendantOf(nodeId: string, ancestorId: string, childrenMap: Map<string | null, TreeNode[]>): boolean;
@@ -111,6 +116,11 @@ declare class FkNodeTreeRowComponent {
111
116
  readonly showDragHandle: _angular_core.InputSignal<boolean>;
112
117
  /** Optional custom template used to render the row content. */
113
118
  readonly customTemplate: _angular_core.InputSignal<TemplateRef<NodeTreeRowContext> | undefined>;
119
+ /** Optional template rendering the row's leading icon (the app owns the icon).
120
+ * Receives the node as `$implicit`; render it from `node.icon`. */
121
+ readonly iconTemplate: _angular_core.InputSignal<TemplateRef<{
122
+ $implicit: FlatNode;
123
+ }> | undefined>;
114
124
  /** Fires when the user clicks the expand/collapse chevron. */
115
125
  readonly toggleNode: _angular_core.OutputEmitterRef<void>;
116
126
  /** Fires when the user clicks the row body to select the node. */
@@ -128,7 +138,7 @@ declare class FkNodeTreeRowComponent {
128
138
  onClick(): void;
129
139
  onToggleClick(event: MouseEvent): void;
130
140
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<FkNodeTreeRowComponent, never>;
131
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<FkNodeTreeRowComponent, "fk-node-tree-row", never, { "className": { "alias": "className"; "required": false; "isSignal": true; }; "node": { "alias": "node"; "required": true; "isSignal": true; }; "selected": { "alias": "selected"; "required": false; "isSignal": true; }; "dropPosition": { "alias": "dropPosition"; "required": false; "isSignal": true; }; "isDragSource": { "alias": "isDragSource"; "required": false; "isSignal": true; }; "showDragHandle": { "alias": "showDragHandle"; "required": false; "isSignal": true; }; "customTemplate": { "alias": "customTemplate"; "required": false; "isSignal": true; }; }, { "toggleNode": "toggleNode"; "selectNode": "selectNode"; }, never, ["*"], true, never>;
141
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<FkNodeTreeRowComponent, "fk-node-tree-row", never, { "className": { "alias": "className"; "required": false; "isSignal": true; }; "node": { "alias": "node"; "required": true; "isSignal": true; }; "selected": { "alias": "selected"; "required": false; "isSignal": true; }; "dropPosition": { "alias": "dropPosition"; "required": false; "isSignal": true; }; "isDragSource": { "alias": "isDragSource"; "required": false; "isSignal": true; }; "showDragHandle": { "alias": "showDragHandle"; "required": false; "isSignal": true; }; "customTemplate": { "alias": "customTemplate"; "required": false; "isSignal": true; }; "iconTemplate": { "alias": "iconTemplate"; "required": false; "isSignal": true; }; }, { "toggleNode": "toggleNode"; "selectNode": "selectNode"; }, never, ["*"], true, never>;
132
142
  }
133
143
 
134
144
  export { FkNodeTreeComponent, FkNodeTreeRowComponent, FkNodeTreeRowDirective, buildChildrenMap, flattenTree, isDescendantOf };
@@ -1,4 +1,5 @@
1
1
  import * as _angular_core from '@angular/core';
2
+ import { TemplateRef } from '@angular/core';
2
3
 
3
4
  type TimelineOrientation = 'vertical' | 'horizontal';
4
5
  type TimelineAlign = 'start' | 'end' | 'alternate';
@@ -16,6 +17,11 @@ interface TimelineItemData {
16
17
  declare class TimelineComponent {
17
18
  /** Array of timeline item data objects rendered in order. */
18
19
  readonly items: _angular_core.InputSignal<TimelineItemData[]>;
20
+ /** Optional template rendering each item's marker icon (the app owns the icon
21
+ * system). Receives the item as `$implicit`; render it from `item.icon`. */
22
+ readonly iconTemplate: _angular_core.InputSignal<TemplateRef<{
23
+ $implicit: TimelineItemData;
24
+ }> | undefined>;
19
25
  /** Direction the timeline runs — vertical stacks items top-to-bottom, horizontal left-to-right. */
20
26
  readonly orientation: _angular_core.InputSignal<TimelineOrientation>;
21
27
  /** Alignment of the item content relative to the connector line. */
@@ -35,7 +41,7 @@ declare class TimelineComponent {
35
41
  get hostAriaOrientation(): TimelineOrientation;
36
42
  get hostId(): string | null;
37
43
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TimelineComponent, never>;
38
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<TimelineComponent, "fk-timeline", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "orientation": { "alias": "orientation"; "required": false; "isSignal": true; }; "align": { "alias": "align"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "reverse": { "alias": "reverse"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
44
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TimelineComponent, "fk-timeline", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "iconTemplate": { "alias": "iconTemplate"; "required": false; "isSignal": true; }; "orientation": { "alias": "orientation"; "required": false; "isSignal": true; }; "align": { "alias": "align"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "reverse": { "alias": "reverse"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
39
45
  }
40
46
 
41
47
  export { TimelineComponent };
@@ -1,10 +1,17 @@
1
1
  import * as _angular_core from '@angular/core';
2
+ import { TemplateRef } from '@angular/core';
2
3
  import { AvatarStatus, AvatarSize } from '@frame-kit/ui-ng/ui/avatar';
3
4
  import { DropdownMenuComponent } from '@frame-kit/ui-ng/ui/dropdown-menu';
4
5
 
5
6
  interface MenuItem {
6
7
  label: string;
7
8
  value: string;
9
+ /**
10
+ * Icon identifier for this item. The widget does **not** render it directly
11
+ * (ui-ng is icon-agnostic) — pass an `iconTemplate` to `fk-user-menu` that
12
+ * reads this field and renders your own icon. Items render without an icon
13
+ * when no `iconTemplate` is supplied.
14
+ */
8
15
  icon?: string;
9
16
  disabled?: boolean;
10
17
  danger?: boolean;
@@ -31,6 +38,14 @@ declare class UserMenuComponent {
31
38
  readonly showStatus: _angular_core.InputSignal<boolean>;
32
39
  /** Array of menu item configurations rendered in the dropdown. */
33
40
  readonly menuItems: _angular_core.InputSignal<MenuItem[]>;
41
+ /**
42
+ * Optional template used to render each item's leading icon (the app owns the
43
+ * icon system). Receives the `MenuItem` as `$implicit`; render the icon from
44
+ * `item.icon`. When omitted, items render without an icon.
45
+ */
46
+ readonly iconTemplate: _angular_core.InputSignal<TemplateRef<{
47
+ $implicit: MenuItem;
48
+ }> | null>;
34
49
  /** Preferred placement of the dropdown panel relative to the trigger. */
35
50
  readonly placement: _angular_core.InputSignal<MenuPlacement>;
36
51
  /** Size of the avatar trigger. */
@@ -80,7 +95,7 @@ declare class UserMenuComponent {
80
95
  /** Toggles the user-menu dropdown programmatically. */
81
96
  toggle(): void;
82
97
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<UserMenuComponent, never>;
83
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<UserMenuComponent, "fk-user-menu", never, { "name": { "alias": "name"; "required": true; "isSignal": true; }; "email": { "alias": "email"; "required": false; "isSignal": true; }; "avatarUrl": { "alias": "avatarUrl"; "required": false; "isSignal": true; }; "initials": { "alias": "initials"; "required": false; "isSignal": true; }; "userId": { "alias": "userId"; "required": false; "isSignal": true; }; "status": { "alias": "status"; "required": false; "isSignal": true; }; "showStatus": { "alias": "showStatus"; "required": false; "isSignal": true; }; "menuItems": { "alias": "menuItems"; "required": false; "isSignal": true; }; "placement": { "alias": "placement"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "showName": { "alias": "showName"; "required": false; "isSignal": true; }; "showEmail": { "alias": "showEmail"; "required": false; "isSignal": true; }; "showChevron": { "alias": "showChevron"; "required": false; "isSignal": true; }; "infoPlacement": { "alias": "infoPlacement"; "required": false; "isSignal": true; }; "badge": { "alias": "badge"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "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; }; }, { "avatarClick": "avatarClick"; "menuOpen": "menuOpen"; "menuClose": "menuClose"; "menuItemClick": "menuItemClick"; "logoutClick": "logoutClick"; }, never, never, true, never>;
98
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<UserMenuComponent, "fk-user-menu", never, { "name": { "alias": "name"; "required": true; "isSignal": true; }; "email": { "alias": "email"; "required": false; "isSignal": true; }; "avatarUrl": { "alias": "avatarUrl"; "required": false; "isSignal": true; }; "initials": { "alias": "initials"; "required": false; "isSignal": true; }; "userId": { "alias": "userId"; "required": false; "isSignal": true; }; "status": { "alias": "status"; "required": false; "isSignal": true; }; "showStatus": { "alias": "showStatus"; "required": false; "isSignal": true; }; "menuItems": { "alias": "menuItems"; "required": false; "isSignal": true; }; "iconTemplate": { "alias": "iconTemplate"; "required": false; "isSignal": true; }; "placement": { "alias": "placement"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "showName": { "alias": "showName"; "required": false; "isSignal": true; }; "showEmail": { "alias": "showEmail"; "required": false; "isSignal": true; }; "showChevron": { "alias": "showChevron"; "required": false; "isSignal": true; }; "infoPlacement": { "alias": "infoPlacement"; "required": false; "isSignal": true; }; "badge": { "alias": "badge"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "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; }; }, { "avatarClick": "avatarClick"; "menuOpen": "menuOpen"; "menuClose": "menuClose"; "menuItemClick": "menuItemClick"; "logoutClick": "logoutClick"; }, never, never, true, never>;
84
99
  }
85
100
 
86
101
  export { UserMenuComponent };
@@ -52,17 +52,16 @@ Applied to an `<ng-template>` that contains menu items. The template is rendered
52
52
 
53
53
  ### fk-menu-item
54
54
 
55
- | Input | Type | Default | Description |
56
- | ---------- | ---------------- | ------- | ----------------------------------------- |
57
- | `icon` | `string \| null` | `null` | Optional icon name shown before the label |
58
- | `danger` | `boolean` | `false` | Applies destructive/danger styling |
59
- | `disabled` | `boolean` | `false` | Disables the item |
55
+ | Input | Type | Default | Description |
56
+ | ---------- | --------- | ------- | ---------------------------------- |
57
+ | `danger` | `boolean` | `false` | Applies destructive/danger styling |
58
+ | `disabled` | `boolean` | `false` | Disables the item |
60
59
 
61
60
  | Output | Type | Description |
62
61
  | ----------- | ------ | ---------------------------------- |
63
62
  | `itemClick` | `void` | Emitted when the item is activated |
64
63
 
65
- The item label is provided through content projection.
64
+ The item label is the default projected content; a leading icon is projected via the `[fkMenuItemIcon]` slot (project your own `fk-icon`). See the `fk-menu-item` README.
66
65
 
67
66
  ### fk-menu-separator
68
67
 
@@ -90,10 +89,10 @@ A visual divider between menu item groups. No inputs or outputs.
90
89
  <fk-dropdown-menu>
91
90
  <button fkDropdownTrigger>Actions</button>
92
91
  <ng-template fkDropdownPanel>
93
- <fk-menu-item icon="eye">View</fk-menu-item>
94
- <fk-menu-item icon="gear-outline">Settings</fk-menu-item>
92
+ <fk-menu-item><fk-icon fkMenuItemIcon name="eye" size="sm" />View</fk-menu-item>
93
+ <fk-menu-item><fk-icon fkMenuItemIcon name="gear-outline" size="sm" />Settings</fk-menu-item>
95
94
  <fk-menu-separator />
96
- <fk-menu-item icon="open-rect-arrow-out-fill" [danger]="true">Delete</fk-menu-item>
95
+ <fk-menu-item [danger]="true"><fk-icon fkMenuItemIcon name="open-rect-arrow-out-fill" size="sm" />Delete</fk-menu-item>
97
96
  </ng-template>
98
97
  </fk-dropdown-menu>
99
98
  ```
@@ -153,10 +152,10 @@ Directives:
153
152
  <fk-dropdown-menu>
154
153
  <button fkDropdownTrigger>Options</button>
155
154
  <ng-template fkDropdownPanel>
156
- <fk-menu-item icon="eye">View</fk-menu-item>
157
- <fk-menu-item icon="gear-outline" [disabled]="true">Settings (locked)</fk-menu-item>
155
+ <fk-menu-item><fk-icon fkMenuItemIcon name="eye" size="sm" />View</fk-menu-item>
156
+ <fk-menu-item [disabled]="true"><fk-icon fkMenuItemIcon name="gear-outline" size="sm" />Settings (locked)</fk-menu-item>
158
157
  <fk-menu-separator />
159
- <fk-menu-item icon="info">Help</fk-menu-item>
158
+ <fk-menu-item><fk-icon fkMenuItemIcon name="info" size="sm" />Help</fk-menu-item>
160
159
  </ng-template>
161
160
  </fk-dropdown-menu>
162
161
  ```
@@ -167,9 +166,9 @@ Directives:
167
166
  <fk-dropdown-menu>
168
167
  <button fkDropdownTrigger>Manage</button>
169
168
  <ng-template fkDropdownPanel>
170
- <fk-menu-item icon="eye">View</fk-menu-item>
169
+ <fk-menu-item><fk-icon fkMenuItemIcon name="eye" size="sm" />View</fk-menu-item>
171
170
  <fk-menu-separator />
172
- <fk-menu-item icon="open-rect-arrow-out-fill" [danger]="true">Remove</fk-menu-item>
171
+ <fk-menu-item [danger]="true"><fk-icon fkMenuItemIcon name="open-rect-arrow-out-fill" size="sm" />Remove</fk-menu-item>
173
172
  </ng-template>
174
173
  </fk-dropdown-menu>
175
174
  ```
@@ -10,11 +10,10 @@ Atom-level menu primitives for rendering actionable items and visual dividers in
10
10
 
11
11
  #### Inputs
12
12
 
13
- | Input | Type | Default | Description |
14
- | ---------- | ---------------- | ------- | ----------------------------------------- |
15
- | `icon` | `string \| null` | `null` | Optional icon name shown before the label |
16
- | `danger` | `boolean` | `false` | Applies destructive/danger styling |
17
- | `disabled` | `boolean` | `false` | Disables the item |
13
+ | Input | Type | Default | Description |
14
+ | ---------- | --------- | ------- | ---------------------------------- |
15
+ | `danger` | `boolean` | `false` | Applies destructive/danger styling |
16
+ | `disabled` | `boolean` | `false` | Disables the item |
18
17
 
19
18
  #### Outputs
20
19
 
@@ -24,12 +23,17 @@ Atom-level menu primitives for rendering actionable items and visual dividers in
24
23
 
25
24
  #### Content
26
25
 
27
- The item label is provided through content projection.
26
+ The item label is the default projected content. A leading icon is projected via the `[fkMenuItemIcon]` slot — the app owns the icon system, so project your own `fk-icon` (or any element):
28
27
 
29
28
  ```html
30
- <fk-menu-item>Save Changes</fk-menu-item>
29
+ <fk-menu-item>
30
+ <fk-icon fkMenuItemIcon name="eye" size="sm" />
31
+ View Details
32
+ </fk-menu-item>
31
33
  ```
32
34
 
35
+ `FkMenuItemIconDirective` (the `[fkMenuItemIcon]` marker) is exported from `@frame-kit/ui-ng`.
36
+
33
37
  ### fk-menu-separator
34
38
 
35
39
  A visual divider between menu item groups. No inputs or outputs.
@@ -42,7 +46,7 @@ A visual divider between menu item groups. No inputs or outputs.
42
46
 
43
47
  ## Features
44
48
 
45
- - Icon + label layout with flexible content projection
49
+ - Label + optional leading-icon slot (`[fkMenuItemIcon]`) via content projection
46
50
  - Danger variant for destructive actions
47
51
  - Disabled state with `aria-disabled` and keyboard exclusion
48
52
  - Keyboard activation via Enter and Space
@@ -55,9 +59,15 @@ A visual divider between menu item groups. No inputs or outputs.
55
59
  ## Quick Start
56
60
 
57
61
  ```html
58
- <fk-menu-item icon="eye" (itemClick)="onView()">View Details</fk-menu-item>
62
+ <fk-menu-item (itemClick)="onView()">
63
+ <fk-icon fkMenuItemIcon name="eye" size="sm" />
64
+ View Details
65
+ </fk-menu-item>
59
66
  <fk-menu-separator />
60
- <fk-menu-item icon="gear-outline" [disabled]="true">Settings</fk-menu-item>
67
+ <fk-menu-item [disabled]="true">
68
+ <fk-icon fkMenuItemIcon name="gear-outline" size="sm" />
69
+ Settings
70
+ </fk-menu-item>
61
71
  <fk-menu-item [danger]="true" (itemClick)="onDelete()">Delete</fk-menu-item>
62
72
  ```
63
73
 
@@ -93,7 +103,10 @@ export class ExampleComponent {}
93
103
  ### With icon
94
104
 
95
105
  ```html
96
- <fk-menu-item icon="eye">View</fk-menu-item>
106
+ <fk-menu-item>
107
+ <fk-icon fkMenuItemIcon name="eye" size="sm" />
108
+ View
109
+ </fk-menu-item>
97
110
  ```
98
111
 
99
112
  ### Without icon
@@ -117,10 +130,10 @@ export class ExampleComponent {}
117
130
  ### With separator
118
131
 
119
132
  ```html
120
- <fk-menu-item icon="eye">View</fk-menu-item>
121
- <fk-menu-item icon="gear-outline">Edit</fk-menu-item>
133
+ <fk-menu-item><fk-icon fkMenuItemIcon name="eye" size="sm" />View</fk-menu-item>
134
+ <fk-menu-item><fk-icon fkMenuItemIcon name="gear-outline" size="sm" />Edit</fk-menu-item>
122
135
  <fk-menu-separator />
123
- <fk-menu-item icon="open-rect-arrow-out-fill" [danger]="true">Delete</fk-menu-item>
136
+ <fk-menu-item [danger]="true"> <fk-icon fkMenuItemIcon name="open-rect-arrow-out-fill" size="sm" />Delete </fk-menu-item>
124
137
  ```
125
138
 
126
139
  ### Handling clicks
@@ -8,14 +8,15 @@ A token-driven, hierarchical tree component for Angular with expand/collapse, se
8
8
 
9
9
  ### Inputs
10
10
 
11
- | Input | Type | Default | Description |
12
- | ----------------- | ------------------------ | ----------- | ------------------------------------------------------------------- |
13
- | `nodes` | `TreeNode[]` | _required_ | Flat array of tree nodes; parent-child relationships via `parentId` |
14
- | `selectedNodeId` | `string \| undefined` | `undefined` | Currently selected node ID |
15
- | `expandedNodeIds` | `string[] \| undefined` | `undefined` | Controlled expanded state; `undefined` uses internal state |
16
- | `draggable` | `boolean` | `false` | Enables drag-and-drop with drag handles |
17
- | `canDrop` | `CanDropFn \| undefined` | `undefined` | Consumer callback to validate drop targets |
18
- | `className` | `string` | `''` | Additional CSS classes for the host |
11
+ | Input | Type | Default | Description |
12
+ | ----------------- | ----------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------- |
13
+ | `nodes` | `TreeNode[]` | _required_ | Flat array of tree nodes; parent-child relationships via `parentId` |
14
+ | `selectedNodeId` | `string \| undefined` | `undefined` | Currently selected node ID |
15
+ | `expandedNodeIds` | `string[] \| undefined` | `undefined` | Controlled expanded state; `undefined` uses internal state |
16
+ | `draggable` | `boolean` | `false` | Enables drag-and-drop with drag handles |
17
+ | `canDrop` | `CanDropFn \| undefined` | `undefined` | Consumer callback to validate drop targets |
18
+ | `iconTemplate` | `TemplateRef<{ $implicit: FlatNode }>` \| `undefined` | `undefined` | Template rendering each row's leading icon (receives the node as `$implicit`); default rows render no icon when omitted |
19
+ | `className` | `string` | `''` | Additional CSS classes for the host |
19
20
 
20
21
  ### Outputs
21
22
 
@@ -31,7 +32,7 @@ A token-driven, hierarchical tree component for Angular with expand/collapse, se
31
32
  interface TreeNode<T = unknown> {
32
33
  id: string;
33
34
  label: string;
34
- icon?: string;
35
+ icon?: string; // rendered only via [iconTemplate] — the tree is icon-agnostic
35
36
  parentId?: string | null;
36
37
  draggable?: boolean;
37
38
  data?: T;
@@ -55,14 +56,28 @@ interface NodeDropEvent {
55
56
  type CanDropFn = (draggedNode: FlatNode, targetNode: FlatNode, position: DropPosition) => boolean;
56
57
  ```
57
58
 
59
+ ### Row icons
60
+
61
+ The tree is icon-agnostic: `node.icon` is only rendered if you supply an `iconTemplate` that reads it (the app owns the icon system). Default rows show no icon without it:
62
+
63
+ ```html
64
+ <ng-template #nodeIcon let-node>
65
+ <fk-icon [name]="node.icon" size="sm" />
66
+ </ng-template>
67
+
68
+ <fk-node-tree [nodes]="nodes" [iconTemplate]="nodeIcon" />
69
+ ```
70
+
58
71
  ### Content Projection
59
72
 
60
- Custom row templates use the `fkNodeTreeRow` structural directive:
73
+ For full control of the row, use the `fkNodeTreeRow` structural directive instead — it replaces the whole row (icon included), so you render your own icon in the template:
61
74
 
62
75
  ```html
63
76
  <fk-node-tree [nodes]="nodes">
64
77
  <ng-template fkNodeTreeRow let-node>
78
+ @if (node.icon) {
65
79
  <fk-icon [name]="node.icon" size="sm" />
80
+ }
66
81
  <span>{{ node.label }}</span>
67
82
  </ng-template>
68
83
  </fk-node-tree>
@@ -8,16 +8,17 @@ A token-driven timeline component for displaying ordered steps, resolution chain
8
8
 
9
9
  ### Inputs
10
10
 
11
- | Input | Type | Default | Description |
12
- | ------------- | --------------------------------- | ------------ | -------------------------------------------- |
13
- | `items` | `TimelineItemData[]` | `[]` | Declarative data for timeline entries |
14
- | `orientation` | `'vertical' \| 'horizontal'` | `'vertical'` | Layout direction |
15
- | `align` | `'start' \| 'end' \| 'alternate'` | `'start'` | Content alignment relative to connector line |
16
- | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Marker and connector sizing |
17
- | `reverse` | `boolean` | `false` | Reverse item order |
18
- | `ariaLabel` | `string \| null` | `null` | Accessible label |
19
- | `id` | `string \| null` | `null` | Element ID |
20
- | `className` | `string` | `''` | Additional CSS classes merged onto the host |
11
+ | Input | Type | Default | Description |
12
+ | -------------- | ------------------------------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------- |
13
+ | `items` | `TimelineItemData[]` | `[]` | Declarative data for timeline entries |
14
+ | `iconTemplate` | `TemplateRef<{ $implicit: TimelineItemData }>` \| `undefined` | `undefined` | Template rendering each item's marker icon (receives the item as `$implicit`); items render no icon when omitted |
15
+ | `orientation` | `'vertical' \| 'horizontal'` | `'vertical'` | Layout direction |
16
+ | `align` | `'start' \| 'end' \| 'alternate'` | `'start'` | Content alignment relative to connector line |
17
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Marker and connector sizing |
18
+ | `reverse` | `boolean` | `false` | Reverse item order |
19
+ | `ariaLabel` | `string \| null` | `null` | Accessible label |
20
+ | `id` | `string \| null` | `null` | Element ID |
21
+ | `className` | `string` | `''` | Additional CSS classes merged onto the host |
21
22
 
22
23
  ### Types
23
24
 
@@ -27,7 +28,7 @@ interface TimelineItemData {
27
28
  title?: string;
28
29
  description?: string;
29
30
  timestamp?: string;
30
- icon?: string;
31
+ icon?: string; // rendered only via [iconTemplate] — the timeline is icon-agnostic
31
32
  status?: TimelineItemStatus;
32
33
  }
33
34
 
@@ -49,7 +50,7 @@ None.
49
50
  - Three alignment modes (start, end, alternate)
50
51
  - Three size presets (sm, md, lg)
51
52
  - Five status variants (default, active, completed, error, warning)
52
- - Custom icon support per item
53
+ - Per-item marker icons via a consumer-supplied `iconTemplate`
53
54
  - Optional timestamp display
54
55
  - Reversible item order
55
56
  - Content projection for complex custom items
@@ -142,8 +143,15 @@ export class ExampleComponent {}
142
143
 
143
144
  ### With icons
144
145
 
146
+ Supply an `iconTemplate` that reads `item.icon` — the timeline never renders `fk-icon` itself:
147
+
145
148
  ```html
149
+ <ng-template #marker let-item>
150
+ <fk-icon [name]="item.icon" size="xs" />
151
+ </ng-template>
152
+
146
153
  <fk-timeline
154
+ [iconTemplate]="marker"
147
155
  [items]="[
148
156
  { title: 'Submitted', icon: 'circle-check-outline', status: 'completed' },
149
157
  { title: 'In Review', icon: 'shield-alert', status: 'active' },
@@ -8,29 +8,30 @@ A token-driven user menu molecule that composes an avatar trigger with a dropdow
8
8
 
9
9
  ### Inputs
10
10
 
11
- | Input | Type | Default | Description |
12
- | ------------- | ----------------------------------------------------------- | -------------- | ------------------------------------------------------------------------------ |
13
- | name | `string` | — | **Required.** User's display name |
14
- | email | `string` \| `null` | `null` | User's email address |
15
- | avatarUrl | `string` \| `null` | `null` | URL to the user's avatar image |
16
- | initials | `string` \| `null` | `null` | Custom initials — auto-derived from `name` if absent |
17
- | userId | `string` \| `null` | `null` | Optional user identifier |
18
- | status | `"online"` \| `"offline"` \| `"away"` \| `"busy"` \| `null` | `null` | Presence status passed to the avatar |
19
- | showStatus | `boolean` | `false` | Show the status indicator on the avatar |
20
- | menuItems | `MenuItem[]` | `[]` | Configurable list of menu actions |
21
- | placement | `"bottom-start"` \| `"bottom-end"` | `"bottom-end"` | Preferred panel alignment — auto-flips and shifts to stay within the viewport |
22
- | size | `"sm"` \| `"md"` \| `"lg"` | `"md"` | Avatar size |
23
- | variant | `"dropdown"` \| `"popover"` | `"dropdown"` | `"popover"` adds a user header inside the panel |
24
- | showName | `boolean` | `false` | Show user name next to the avatar in the trigger |
25
- | showEmail | `boolean` | `false` | Show email next to the avatar in the trigger |
26
- | showChevron | `boolean` | `true` | Show a chevron indicator on the trigger |
27
- | infoPlacement | `"start"` \| `"end"` | `"end"` | Position name/email relative to the avatar — `"start"` places text on the left |
28
- | badge | `string` \| `null` | `null` | Badge label (e.g. "Admin", "Pro") |
29
- | disabled | `boolean` | `false` | Disable the trigger |
30
- | loading | `boolean` | `false` | Disable the trigger during loading |
31
- | className | `string` | `""` | Additional CSS classes merged with hook classes |
32
- | id | `string` \| `null` | `null` | Sets the host `id` attribute |
33
- | ariaLabel | `string` \| `null` | `null` | Custom `aria-label` (defaults to "User menu for {name}") |
11
+ | Input | Type | Default | Description |
12
+ | ------------- | ----------------------------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------- |
13
+ | name | `string` | — | **Required.** User's display name |
14
+ | email | `string` \| `null` | `null` | User's email address |
15
+ | avatarUrl | `string` \| `null` | `null` | URL to the user's avatar image |
16
+ | initials | `string` \| `null` | `null` | Custom initials — auto-derived from `name` if absent |
17
+ | userId | `string` \| `null` | `null` | Optional user identifier |
18
+ | status | `"online"` \| `"offline"` \| `"away"` \| `"busy"` \| `null` | `null` | Presence status passed to the avatar |
19
+ | showStatus | `boolean` | `false` | Show the status indicator on the avatar |
20
+ | menuItems | `MenuItem[]` | `[]` | Configurable list of menu actions |
21
+ | iconTemplate | `TemplateRef<{ $implicit: MenuItem }>` \| `undefined` | `undefined` | Template rendering each item's leading icon (receives the item as `$implicit`); items render without an icon when omitted |
22
+ | placement | `"bottom-start"` \| `"bottom-end"` | `"bottom-end"` | Preferred panel alignment — auto-flips and shifts to stay within the viewport |
23
+ | size | `"sm"` \| `"md"` \| `"lg"` | `"md"` | Avatar size |
24
+ | variant | `"dropdown"` \| `"popover"` | `"dropdown"` | `"popover"` adds a user header inside the panel |
25
+ | showName | `boolean` | `false` | Show user name next to the avatar in the trigger |
26
+ | showEmail | `boolean` | `false` | Show email next to the avatar in the trigger |
27
+ | showChevron | `boolean` | `true` | Show a chevron indicator on the trigger |
28
+ | infoPlacement | `"start"` \| `"end"` | `"end"` | Position name/email relative to the avatar — `"start"` places text on the left |
29
+ | badge | `string` \| `null` | `null` | Badge label (e.g. "Admin", "Pro") |
30
+ | disabled | `boolean` | `false` | Disable the trigger |
31
+ | loading | `boolean` | `false` | Disable the trigger during loading |
32
+ | className | `string` | `""` | Additional CSS classes merged with hook classes |
33
+ | id | `string` \| `null` | `null` | Sets the host `id` attribute |
34
+ | ariaLabel | `string` \| `null` | `null` | Custom `aria-label` (defaults to "User menu for {name}") |
34
35
 
35
36
  ### Outputs
36
37
 
@@ -48,7 +49,7 @@ A token-driven user menu molecule that composes an avatar trigger with a dropdow
48
49
  interface MenuItem {
49
50
  label: string;
50
51
  value: string;
51
- icon?: string;
52
+ icon?: string; // rendered only via [iconTemplate] — see below
52
53
  disabled?: boolean;
53
54
  danger?: boolean;
54
55
  separator?: boolean;
@@ -57,12 +58,14 @@ interface MenuItem {
57
58
 
58
59
  When `separator` is `true`, the item renders as a horizontal divider instead of a button.
59
60
 
61
+ `fk-user-menu` is icon-agnostic: `icon` is just a string your `iconTemplate` reads — the widget never renders `fk-icon` itself. Without an `iconTemplate`, items show no icon.
62
+
60
63
  ---
61
64
 
62
65
  ## Features
63
66
 
64
67
  - Composes `fk-avatar` for the trigger display
65
- - Configurable menu items with icons, danger styling, disabled state, and separators
68
+ - Configurable menu items with danger styling, disabled state, separators, and consumer-supplied icons (via `iconTemplate`)
66
69
  - Two variants: `dropdown` (items only) and `popover` (user header + items)
67
70
  - Optional name, email, badge, and chevron in the trigger with configurable info placement (`start` or `end`)
68
71
  - Viewport-aware positioning — panel auto-flips vertically and shifts horizontally to avoid clipping
@@ -75,7 +78,12 @@ When `separator` is `true`, the item renders as a horizontal divider instead of
75
78
  ## Quick Start
76
79
 
77
80
  ```html
78
- <fk-user-menu name="Jane Doe" [menuItems]="menuItems" (menuItemClick)="onMenuItem($event)" (logoutClick)="onLogout()" />
81
+ <!-- Supply an icon template that reads item.icon; the app owns the icon system. -->
82
+ <ng-template #menuIcon let-item>
83
+ <fk-icon [name]="item.icon" size="sm" />
84
+ </ng-template>
85
+
86
+ <fk-user-menu name="Jane Doe" [menuItems]="menuItems" [iconTemplate]="menuIcon" (menuItemClick)="onMenuItem($event)" (logoutClick)="onLogout()" />
79
87
  ```
80
88
 
81
89
  ```ts
@@ -87,6 +95,8 @@ menuItems: MenuItem[] = [
87
95
  ];
88
96
  ```
89
97
 
98
+ Omit `[iconTemplate]` (and the `icon` fields) if you don't need icons.
99
+
90
100
  ---
91
101
 
92
102
  ## Import