@frame-kit/ui-ng 0.0.1
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/COMPONENTS.md +683 -0
- package/DEVELOPMENT_GUIDE.md +1102 -0
- package/LICENSE +21 -0
- package/README.md +69 -0
- package/THEMING.md +130 -0
- package/core/headline/README.md +121 -0
- package/core/icon/README.md +173 -0
- package/core/image/README.md +210 -0
- package/core/link/README.md +297 -0
- package/core/separator/README.md +145 -0
- package/core/text/README.md +240 -0
- package/directives/infinite-scroll/README.md +102 -0
- package/directives/spotlight/README.md +154 -0
- package/directives/tooltip/README.md +147 -0
- package/docs/endpoint-link/README.md +142 -0
- package/docs/method-badge/README.md +154 -0
- package/fesm2022/frame-kit-ui-ng-core-headline.mjs +122 -0
- package/fesm2022/frame-kit-ui-ng-core-headline.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-core-icon.mjs +189 -0
- package/fesm2022/frame-kit-ui-ng-core-icon.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-core-image.mjs +123 -0
- package/fesm2022/frame-kit-ui-ng-core-image.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-core-link.mjs +369 -0
- package/fesm2022/frame-kit-ui-ng-core-link.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-core-separator.mjs +59 -0
- package/fesm2022/frame-kit-ui-ng-core-separator.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-core-text.mjs +204 -0
- package/fesm2022/frame-kit-ui-ng-core-text.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-directives-infinite-scroll.mjs +74 -0
- package/fesm2022/frame-kit-ui-ng-directives-infinite-scroll.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-directives-spotlight.mjs +76 -0
- package/fesm2022/frame-kit-ui-ng-directives-spotlight.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-directives-tooltip.mjs +425 -0
- package/fesm2022/frame-kit-ui-ng-directives-tooltip.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-docs-endpoint-link.mjs +63 -0
- package/fesm2022/frame-kit-ui-ng-docs-endpoint-link.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-docs-method-badge.mjs +43 -0
- package/fesm2022/frame-kit-ui-ng-docs-method-badge.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-forms.mjs +3632 -0
- package/fesm2022/frame-kit-ui-ng-forms.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-layouts-app-shell.mjs +239 -0
- package/fesm2022/frame-kit-ui-ng-layouts-app-shell.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-layouts-content-split.mjs +132 -0
- package/fesm2022/frame-kit-ui-ng-layouts-content-split.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-services-overlay-orchestrator.mjs +133 -0
- package/fesm2022/frame-kit-ui-ng-services-overlay-orchestrator.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-services-spotlight.mjs +60 -0
- package/fesm2022/frame-kit-ui-ng-services-spotlight.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-services-toast.mjs +166 -0
- package/fesm2022/frame-kit-ui-ng-services-toast.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-accordion.mjs +214 -0
- package/fesm2022/frame-kit-ui-ng-ui-accordion.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-alert.mjs +82 -0
- package/fesm2022/frame-kit-ui-ng-ui-alert.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-avatar-stack.mjs +76 -0
- package/fesm2022/frame-kit-ui-ng-ui-avatar-stack.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-avatar.mjs +81 -0
- package/fesm2022/frame-kit-ui-ng-ui-avatar.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-badge.mjs +81 -0
- package/fesm2022/frame-kit-ui-ng-ui-badge.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-breadcrumb.mjs +68 -0
- package/fesm2022/frame-kit-ui-ng-ui-breadcrumb.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-button.mjs +108 -0
- package/fesm2022/frame-kit-ui-ng-ui-button.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-callout.mjs +58 -0
- package/fesm2022/frame-kit-ui-ng-ui-callout.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-card.mjs +70 -0
- package/fesm2022/frame-kit-ui-ng-ui-card.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-copyable-field.mjs +113 -0
- package/fesm2022/frame-kit-ui-ng-ui-copyable-field.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-data-table.mjs +1288 -0
- package/fesm2022/frame-kit-ui-ng-ui-data-table.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-dialog.mjs +456 -0
- package/fesm2022/frame-kit-ui-ng-ui-dialog.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-drawer.mjs +398 -0
- package/fesm2022/frame-kit-ui-ng-ui-drawer.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-dropdown-menu.mjs +398 -0
- package/fesm2022/frame-kit-ui-ng-ui-dropdown-menu.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-editable-field.mjs +125 -0
- package/fesm2022/frame-kit-ui-ng-ui-editable-field.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-icon-badge.mjs +113 -0
- package/fesm2022/frame-kit-ui-ng-ui-icon-badge.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-icon-list.mjs +111 -0
- package/fesm2022/frame-kit-ui-ng-ui-icon-list.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-inline-edit.mjs +103 -0
- package/fesm2022/frame-kit-ui-ng-ui-inline-edit.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-list-editor.mjs +135 -0
- package/fesm2022/frame-kit-ui-ng-ui-list-editor.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-loader.mjs +81 -0
- package/fesm2022/frame-kit-ui-ng-ui-loader.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-menu-item.mjs +79 -0
- package/fesm2022/frame-kit-ui-ng-ui-menu-item.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-nav-brand.mjs +40 -0
- package/fesm2022/frame-kit-ui-ng-ui-nav-brand.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-nav-group.mjs +110 -0
- package/fesm2022/frame-kit-ui-ng-ui-nav-group.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-nav-separator.mjs +91 -0
- package/fesm2022/frame-kit-ui-ng-ui-nav-separator.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-node-tree-breadcrumb.mjs +86 -0
- package/fesm2022/frame-kit-ui-ng-ui-node-tree-breadcrumb.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-node-tree.mjs +443 -0
- package/fesm2022/frame-kit-ui-ng-ui-node-tree.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-note.mjs +56 -0
- package/fesm2022/frame-kit-ui-ng-ui-note.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-numbered-list.mjs +105 -0
- package/fesm2022/frame-kit-ui-ng-ui-numbered-list.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-pagination.mjs +110 -0
- package/fesm2022/frame-kit-ui-ng-ui-pagination.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-progress-bar.mjs +129 -0
- package/fesm2022/frame-kit-ui-ng-ui-progress-bar.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-sidenav-link.mjs +42 -0
- package/fesm2022/frame-kit-ui-ng-ui-sidenav-link.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-tabs.mjs +894 -0
- package/fesm2022/frame-kit-ui-ng-ui-tabs.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-timeline.mjs +81 -0
- package/fesm2022/frame-kit-ui-ng-ui-timeline.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-toast.mjs +179 -0
- package/fesm2022/frame-kit-ui-ng-ui-toast.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-user-menu.mjs +143 -0
- package/fesm2022/frame-kit-ui-ng-ui-user-menu.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-wizard-dialog.mjs +191 -0
- package/fesm2022/frame-kit-ui-ng-ui-wizard-dialog.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng.mjs +58 -0
- package/fesm2022/frame-kit-ui-ng.mjs.map +1 -0
- package/layouts/app-shell/README.md +357 -0
- package/layouts/content-split/README.md +180 -0
- package/package.json +253 -0
- package/services/overlay-orchestrator/README.md +184 -0
- package/services/spotlight/README.md +61 -0
- package/services/toast/README.md +118 -0
- package/types/frame-kit-ui-ng-core-headline.d.ts +38 -0
- package/types/frame-kit-ui-ng-core-icon.d.ts +74 -0
- package/types/frame-kit-ui-ng-core-image.d.ts +93 -0
- package/types/frame-kit-ui-ng-core-link.d.ts +251 -0
- package/types/frame-kit-ui-ng-core-separator.d.ts +28 -0
- package/types/frame-kit-ui-ng-core-text.d.ts +186 -0
- package/types/frame-kit-ui-ng-directives-infinite-scroll.d.ts +42 -0
- package/types/frame-kit-ui-ng-directives-spotlight.d.ts +51 -0
- package/types/frame-kit-ui-ng-directives-tooltip.d.ts +70 -0
- package/types/frame-kit-ui-ng-docs-endpoint-link.d.ts +43 -0
- package/types/frame-kit-ui-ng-docs-method-badge.d.ts +30 -0
- package/types/frame-kit-ui-ng-forms.d.ts +1674 -0
- package/types/frame-kit-ui-ng-layouts-app-shell.d.ts +75 -0
- package/types/frame-kit-ui-ng-layouts-content-split.d.ts +43 -0
- package/types/frame-kit-ui-ng-services-overlay-orchestrator.d.ts +96 -0
- package/types/frame-kit-ui-ng-services-spotlight.d.ts +32 -0
- package/types/frame-kit-ui-ng-services-toast.d.ts +100 -0
- package/types/frame-kit-ui-ng-ui-accordion.d.ts +86 -0
- package/types/frame-kit-ui-ng-ui-alert.d.ts +34 -0
- package/types/frame-kit-ui-ng-ui-avatar-stack.d.ts +38 -0
- package/types/frame-kit-ui-ng-ui-avatar.d.ts +36 -0
- package/types/frame-kit-ui-ng-ui-badge.d.ts +33 -0
- package/types/frame-kit-ui-ng-ui-breadcrumb.d.ts +45 -0
- package/types/frame-kit-ui-ng-ui-button.d.ts +48 -0
- package/types/frame-kit-ui-ng-ui-callout.d.ts +26 -0
- package/types/frame-kit-ui-ng-ui-card.d.ts +30 -0
- package/types/frame-kit-ui-ng-ui-copyable-field.d.ts +62 -0
- package/types/frame-kit-ui-ng-ui-data-table.d.ts +482 -0
- package/types/frame-kit-ui-ng-ui-dialog.d.ts +166 -0
- package/types/frame-kit-ui-ng-ui-drawer.d.ts +130 -0
- package/types/frame-kit-ui-ng-ui-dropdown-menu.d.ts +77 -0
- package/types/frame-kit-ui-ng-ui-editable-field.d.ts +65 -0
- package/types/frame-kit-ui-ng-ui-icon-badge.d.ts +45 -0
- package/types/frame-kit-ui-ng-ui-icon-list.d.ts +67 -0
- package/types/frame-kit-ui-ng-ui-inline-edit.d.ts +44 -0
- package/types/frame-kit-ui-ng-ui-list-editor.d.ts +56 -0
- package/types/frame-kit-ui-ng-ui-loader.d.ts +32 -0
- package/types/frame-kit-ui-ng-ui-menu-item.d.ts +27 -0
- package/types/frame-kit-ui-ng-ui-nav-brand.d.ts +25 -0
- package/types/frame-kit-ui-ng-ui-nav-group.d.ts +60 -0
- package/types/frame-kit-ui-ng-ui-nav-separator.d.ts +33 -0
- package/types/frame-kit-ui-ng-ui-node-tree-breadcrumb.d.ts +35 -0
- package/types/frame-kit-ui-ng-ui-node-tree.d.ts +135 -0
- package/types/frame-kit-ui-ng-ui-note.d.ts +22 -0
- package/types/frame-kit-ui-ng-ui-numbered-list.d.ts +52 -0
- package/types/frame-kit-ui-ng-ui-pagination.d.ts +49 -0
- package/types/frame-kit-ui-ng-ui-progress-bar.d.ts +50 -0
- package/types/frame-kit-ui-ng-ui-sidenav-link.d.ts +24 -0
- package/types/frame-kit-ui-ng-ui-tabs.d.ts +266 -0
- package/types/frame-kit-ui-ng-ui-timeline.d.ts +42 -0
- package/types/frame-kit-ui-ng-ui-toast.d.ts +56 -0
- package/types/frame-kit-ui-ng-ui-user-menu.d.ts +87 -0
- package/types/frame-kit-ui-ng-ui-wizard-dialog.d.ts +116 -0
- package/types/frame-kit-ui-ng.d.ts +53 -0
- package/ui/accordion/README.md +261 -0
- package/ui/alert/README.md +211 -0
- package/ui/avatar/README.md +167 -0
- package/ui/avatar-stack/README.md +164 -0
- package/ui/badge/README.md +162 -0
- package/ui/breadcrumb/README.md +240 -0
- package/ui/button/README.md +184 -0
- package/ui/callout/README.md +159 -0
- package/ui/card/README.md +174 -0
- package/ui/copyable-field/README.md +235 -0
- package/ui/data-table/README.md +408 -0
- package/ui/dialog/README.md +222 -0
- package/ui/drawer/README.md +274 -0
- package/ui/dropdown-menu/README.md +336 -0
- package/ui/editable-field/README.md +171 -0
- package/ui/icon-badge/README.md +131 -0
- package/ui/icon-list/README.md +205 -0
- package/ui/inline-edit/README.md +135 -0
- package/ui/list-editor/README.md +162 -0
- package/ui/loader/README.md +160 -0
- package/ui/menu-item/README.md +204 -0
- package/ui/nav-brand/README.md +111 -0
- package/ui/nav-group/README.md +145 -0
- package/ui/nav-separator/README.md +44 -0
- package/ui/node-tree/README.md +278 -0
- package/ui/node-tree-breadcrumb/README.md +164 -0
- package/ui/note/README.md +146 -0
- package/ui/numbered-list/README.md +187 -0
- package/ui/pagination/README.md +174 -0
- package/ui/progress-bar/README.md +223 -0
- package/ui/sidenav-link/README.md +214 -0
- package/ui/tabs/README.md +204 -0
- package/ui/timeline/README.md +285 -0
- package/ui/toast/README.md +243 -0
- package/ui/user-menu/README.md +260 -0
- package/ui/wizard-dialog/README.md +283 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# fk-copyable-field
|
|
2
|
+
|
|
3
|
+
A read-only value paired with a copy-to-clipboard button. Use it when a
|
|
4
|
+
user needs to copy a secret, token, ID, URL, or any value that's easier to
|
|
5
|
+
copy than to retype — API keys, OAuth client IDs, session tokens, signed
|
|
6
|
+
URLs, etc.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## API
|
|
11
|
+
|
|
12
|
+
### Inputs
|
|
13
|
+
|
|
14
|
+
| Input | Type | Default | Description |
|
|
15
|
+
| -------------------- | ------------------- | ----------------------- | -------------------------------------------------------------------------- |
|
|
16
|
+
| `value` | `string` | _required_ | The text that gets written to the clipboard |
|
|
17
|
+
| `label` | `string \| null` | `null` | Optional label rendered above the field |
|
|
18
|
+
| `size` | `CopyableFieldSize` | `"md"` | Size modifier — `"sm"`, `"md"`, or `"lg"` |
|
|
19
|
+
| `mono` | `boolean` | `true` | Render the value in a monospace font |
|
|
20
|
+
| `copyAriaLabel` | `string` | `"Copy to clipboard"` | Accessible label for the copy button |
|
|
21
|
+
| `copiedAnnouncement` | `string` | `"Copied to clipboard"` | Message announced via `role="status"` after a successful copy |
|
|
22
|
+
| `copiedDuration` | `number` | `2000` | Milliseconds before the icon reverts and the announcement clears |
|
|
23
|
+
| `id` | `string \| null` | `null` | Optional ID applied to the value element |
|
|
24
|
+
| `ariaLabel` | `string \| null` | `null` | Accessible label for the value element (use when the label prop is absent) |
|
|
25
|
+
| `className` | `string` | `""` | Additional CSS classes merged onto the host element |
|
|
26
|
+
|
|
27
|
+
### Outputs
|
|
28
|
+
|
|
29
|
+
| Output | Type | Description |
|
|
30
|
+
| ------------ | --------- | -------------------------------------------------------- |
|
|
31
|
+
| `copied` | `string` | Emits the copied value when the clipboard write succeeds |
|
|
32
|
+
| `copyFailed` | `unknown` | Emits the rejection error when the clipboard API rejects |
|
|
33
|
+
|
|
34
|
+
### Content Projection Slots
|
|
35
|
+
|
|
36
|
+
| Attribute | Description |
|
|
37
|
+
| -------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
38
|
+
| `fkCopyIcon` | Optional custom icon shown on the copy button in the idle state. Defaults to an inline clipboard SVG. |
|
|
39
|
+
| `fkCopiedIcon` | Optional custom icon shown for `copiedDuration` ms after a successful copy. Defaults to an inline checkmark SVG. |
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<fk-copyable-field [value]="apiKey">
|
|
43
|
+
<fk-icon fkCopyIcon name="clipboard-outline" size="sm" />
|
|
44
|
+
<fk-icon fkCopiedIcon name="check-outline" size="sm" />
|
|
45
|
+
</fk-copyable-field>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If a slot is not populated, the library renders its own inline SVG —
|
|
49
|
+
consumers don't have to register anything to get a visible button.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Features
|
|
54
|
+
|
|
55
|
+
- One-click copy to the system clipboard via `navigator.clipboard`
|
|
56
|
+
- Icon swap + screen-reader announcement confirm a successful copy
|
|
57
|
+
- Auto-reverts after `copiedDuration` ms
|
|
58
|
+
- Emits `copied` / `copyFailed` for analytics or custom UX
|
|
59
|
+
- Three sizes (`sm`, `md`, `lg`) and optional monospace value styling
|
|
60
|
+
- Token-driven styling with two-tier fallbacks
|
|
61
|
+
- Renders correctly with no theme file loaded
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Quick Start
|
|
66
|
+
|
|
67
|
+
```html
|
|
68
|
+
<fk-copyable-field label="API key" [value]="apiKey" />
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Import
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { CopyableFieldComponent } from '@frame-kit/ui-ng';
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
@Component({
|
|
81
|
+
selector: 'app-example',
|
|
82
|
+
imports: [CopyableFieldComponent],
|
|
83
|
+
templateUrl: './example.component.html',
|
|
84
|
+
})
|
|
85
|
+
export class ExampleComponent {}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Selector
|
|
91
|
+
|
|
92
|
+
```html
|
|
93
|
+
<fk-copyable-field [value]="..." />
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Examples
|
|
99
|
+
|
|
100
|
+
### Basic API key field
|
|
101
|
+
|
|
102
|
+
```html
|
|
103
|
+
<fk-copyable-field label="Your new API key" [value]="apiKey" />
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Non-mono (plain text)
|
|
107
|
+
|
|
108
|
+
```html
|
|
109
|
+
<fk-copyable-field label="Org name" [mono]="false" value="Acme Realty" />
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Size variants
|
|
113
|
+
|
|
114
|
+
```html
|
|
115
|
+
<fk-copyable-field size="sm" value="sm_value" />
|
|
116
|
+
<fk-copyable-field size="md" value="md_value" />
|
|
117
|
+
<fk-copyable-field size="lg" value="lg_value" />
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Reacting to a successful copy
|
|
121
|
+
|
|
122
|
+
```html
|
|
123
|
+
<fk-copyable-field label="Client secret" [value]="secret" (copied)="onCopied($event)" (copyFailed)="onCopyFailed($event)" />
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Icon overrides
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
<fk-copyable-field [value]="value">
|
|
130
|
+
<fk-icon fkCopyIcon name="clipboard-outline" size="sm" />
|
|
131
|
+
<fk-icon fkCopiedIcon name="check-outline" size="sm" />
|
|
132
|
+
</fk-copyable-field>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The projected element just needs the `fkCopyIcon` / `fkCopiedIcon`
|
|
136
|
+
attribute — it doesn't have to be `fk-icon`. Any inline SVG, icon
|
|
137
|
+
component, or even plain text works.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Accessibility
|
|
142
|
+
|
|
143
|
+
- The copy button is a real `<button>` element with `aria-label` from the
|
|
144
|
+
`copyAriaLabel` input — keyboard-reachable, Enter / Space activate it
|
|
145
|
+
- After a successful copy, a visually-hidden element with
|
|
146
|
+
`role="status"` and `aria-live="polite"` announces
|
|
147
|
+
`copiedAnnouncement` ("Copied to clipboard" by default)
|
|
148
|
+
- Icons are marked `aria-hidden="true"` — all accessible text lives on
|
|
149
|
+
the button's label
|
|
150
|
+
- Focus ring uses `--fk-focus-ring`
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Design Tokens
|
|
155
|
+
|
|
156
|
+
`fk-copyable-field` is styled entirely through design tokens.
|
|
157
|
+
|
|
158
|
+
### Component-specific tokens
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
--fk-copyable-field-gap
|
|
162
|
+
--fk-copyable-field-bg
|
|
163
|
+
--fk-copyable-field-border-color
|
|
164
|
+
--fk-copyable-field-radius
|
|
165
|
+
--fk-copyable-field-value-color
|
|
166
|
+
|
|
167
|
+
--fk-copyable-field-label-color
|
|
168
|
+
--fk-copyable-field-label-font-size
|
|
169
|
+
--fk-copyable-field-label-font-weight
|
|
170
|
+
--fk-copyable-field-label-gap
|
|
171
|
+
|
|
172
|
+
--fk-copyable-field-action-color
|
|
173
|
+
--fk-copyable-field-action-color-hover
|
|
174
|
+
--fk-copyable-field-action-bg-hover
|
|
175
|
+
--fk-copyable-field-action-padding
|
|
176
|
+
--fk-copyable-field-action-radius
|
|
177
|
+
|
|
178
|
+
--fk-copyable-field-padding-sm
|
|
179
|
+
--fk-copyable-field-padding-md
|
|
180
|
+
--fk-copyable-field-padding-lg
|
|
181
|
+
--fk-copyable-field-font-size-sm
|
|
182
|
+
--fk-copyable-field-font-size-md
|
|
183
|
+
--fk-copyable-field-font-size-lg
|
|
184
|
+
--fk-copyable-field-mono-font-family
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Shared tokens
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
--fk-rhythm-1
|
|
191
|
+
--fk-rhythm-2
|
|
192
|
+
--fk-rhythm-3
|
|
193
|
+
--fk-rhythm-4
|
|
194
|
+
--fk-color-text
|
|
195
|
+
--fk-color-muted
|
|
196
|
+
--fk-color-border
|
|
197
|
+
--fk-color-surface-muted
|
|
198
|
+
--fk-color-surface-dim
|
|
199
|
+
--fk-font-family-mono
|
|
200
|
+
--fk-typography-caption-font-size
|
|
201
|
+
--fk-typography-small-font-size
|
|
202
|
+
--fk-typography-body-font-size
|
|
203
|
+
--fk-font-weight-medium
|
|
204
|
+
--fk-radius-md
|
|
205
|
+
--fk-radius-sm
|
|
206
|
+
--fk-focus-ring
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Customizing Tokens
|
|
210
|
+
|
|
211
|
+
```scss
|
|
212
|
+
:root {
|
|
213
|
+
--fk-copyable-field-bg: #0f1626;
|
|
214
|
+
--fk-copyable-field-border-color: #1e2a44;
|
|
215
|
+
--fk-copyable-field-value-color: #e6edf7;
|
|
216
|
+
--fk-copyable-field-action-color: #8b98b5;
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Behavior Notes
|
|
223
|
+
|
|
224
|
+
- Uses `navigator.clipboard.writeText(value)`; when the API is unavailable
|
|
225
|
+
or the user denies clipboard permission, `copyFailed` fires with the
|
|
226
|
+
caught error and the button's icon does not swap
|
|
227
|
+
- The "copied" state is local to the component — rapidly clicking the
|
|
228
|
+
button resets the revert timer so the confirmation always stays for
|
|
229
|
+
`copiedDuration` ms after the most recent click
|
|
230
|
+
- Long values wrap with `word-break: break-all` so the entire value stays
|
|
231
|
+
visible rather than being truncated
|
|
232
|
+
- The component ships with inline SVG icons for both idle and copied
|
|
233
|
+
states, so it renders a visible button with no consumer setup. Override
|
|
234
|
+
either by projecting an element carrying the `fkCopyIcon` or
|
|
235
|
+
`fkCopiedIcon` attribute
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
# fk-data-table
|
|
2
|
+
|
|
3
|
+
A token-driven, responsive data table component for Angular with sorting, virtual scrolling, column resizing, column pinning, multi-row selection (controlled or uncontrolled), and a mobile stacked card view.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## API
|
|
8
|
+
|
|
9
|
+
### Inputs
|
|
10
|
+
|
|
11
|
+
| Input | Type | Default | Description |
|
|
12
|
+
| ------------------------- | ---------------------------------- | -------------- | ---------------------------------------------------------------------------------------------------- |
|
|
13
|
+
| `columns` | `ColumnDef<T>[]` | `[]` | Column definitions describing headers, accessors, and behavior |
|
|
14
|
+
| `rows` | `T[]` | `[]` | Data rows to render |
|
|
15
|
+
| `config` | `Partial<TableConfig<T>>` | `{}` | Table configuration merged with defaults |
|
|
16
|
+
| `loading` | `boolean` | `false` | Shows a loading overlay |
|
|
17
|
+
| `sortState` | `SortState \| null` | `null` | Controlled sort state; `null` uses internal state |
|
|
18
|
+
| `filterState` | `FilterState \| null` | `null` | Controlled filter state; `null` uses internal state |
|
|
19
|
+
| `pageState` | `PageState \| null` | `null` | Controlled page state; `null` uses internal state |
|
|
20
|
+
| `rowSelection` | `boolean` | `false` | Render a leading (or trailing) checkbox column with multi-row selection |
|
|
21
|
+
| `selectedKeys` | `Set<unknown> \| null` | `null` | Controlled selection set; `null` uses internal state. Table never mutates the input — emits new Sets |
|
|
22
|
+
| `getRowId` | `(row: T) => unknown` \| `null` | `null` | Required when `rowSelection` is `true`. Returns each row's stable identity key |
|
|
23
|
+
| `selectionDisabled` | `boolean \| ((row: T) => boolean)` | `false` | Per-row opt-out. Disabled rows can't be selected and are skipped by the header "select all" |
|
|
24
|
+
| `selectionColumnPosition` | `'start' \| 'end'` | `'start'` | Render the checkbox column at the leading or trailing edge |
|
|
25
|
+
| `selectionLabel` | `string` | `'Select row'` | `aria-label` applied to each row checkbox |
|
|
26
|
+
| `className` | `string` | `''` | Additional CSS classes for the host |
|
|
27
|
+
| `id` | `string \| null` | `null` | Optional ID for the host element |
|
|
28
|
+
| `ariaLabel` | `string \| null` | `null` | ARIA label for the table |
|
|
29
|
+
|
|
30
|
+
### Outputs
|
|
31
|
+
|
|
32
|
+
| Output | Type | Description |
|
|
33
|
+
| ----------------- | -------------------- | ------------------------------------------------------------------------------------ |
|
|
34
|
+
| `sortChange` | `SortState` | Emitted when a sortable column header is clicked |
|
|
35
|
+
| `filterChange` | `FilterState` | Emitted when filter state changes |
|
|
36
|
+
| `pageChange` | `PageState` | Emitted when page state changes |
|
|
37
|
+
| `rowClick` | `TableRowContext<T>` | Emitted when a row is clicked (excludes checkbox clicks) |
|
|
38
|
+
| `selectionChange` | `Set<unknown>` | Emitted on every selection change. The Set is brand-new — the input is never mutated |
|
|
39
|
+
| `columnResize` | `ColumnResizeEvent` | Emitted when a resizable column is resized |
|
|
40
|
+
|
|
41
|
+
### ColumnDef
|
|
42
|
+
|
|
43
|
+
| Property | Type | Default | Description |
|
|
44
|
+
| ---------------- | -------------------------------- | ---------- | -------------------------------------------------------------- |
|
|
45
|
+
| `id` | `string` | _required_ | Unique column identifier |
|
|
46
|
+
| `header` | `string` | _required_ | Header text |
|
|
47
|
+
| `accessor` | `keyof T \| (row: T) => unknown` | _required_ | Key or function to extract cell value from a row |
|
|
48
|
+
| `sortAccessor` | `(row: T) => unknown` | — | Custom accessor used for sorting instead of `accessor` |
|
|
49
|
+
| `width` | `string` | — | Explicit column width (e.g. `'200px'`) |
|
|
50
|
+
| `minWidth` | `string` | — | Minimum column width |
|
|
51
|
+
| `maxWidth` | `string` | — | Maximum column width |
|
|
52
|
+
| `flex` | `number` | — | Flex value for virtual/div-based render path |
|
|
53
|
+
| `sortable` | `boolean` | — | Enable sorting on this column |
|
|
54
|
+
| `align` | `'start' \| 'center' \| 'end'` | `'start'` | Text alignment |
|
|
55
|
+
| `truncate` | `boolean` | — | Truncate cell text with ellipsis |
|
|
56
|
+
| `visible` | `boolean` | `true` | Whether the column is visible |
|
|
57
|
+
| `cellRenderer` | `TemplateRef` | — | Custom cell template; context: `{ $implicit: value, row: T }` |
|
|
58
|
+
| `headerRenderer` | `TemplateRef` | — | Custom header template; context: `{ $implicit: ColumnDef<T> }` |
|
|
59
|
+
| `priority` | `number` | — | Priority for responsive column hiding (lower = kept longer) |
|
|
60
|
+
| `hideBelow` | `'sm' \| 'md' \| 'lg'` | — | Hide this column below the specified breakpoint |
|
|
61
|
+
| `mobileLabel` | `string` | — | Label used in the stacked mobile card view |
|
|
62
|
+
| `resizable` | `boolean` | — | Enable drag-to-resize on this column |
|
|
63
|
+
| `pinnable` | `boolean` | — | Mark column as eligible for pinning |
|
|
64
|
+
| `pinned` | `'start' \| 'end' \| null` | `null` | Pin column to the start or end of the table |
|
|
65
|
+
|
|
66
|
+
### TableConfig
|
|
67
|
+
|
|
68
|
+
| Property | Type | Default | Description |
|
|
69
|
+
| ------------------------- | --------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------- |
|
|
70
|
+
| `responsiveMode` | `'auto' \| 'scroll' \| 'stack' \| 'priority'` | `'auto'` | How the table adapts at smaller viewports |
|
|
71
|
+
| `forceScroll` | `boolean` | `false` | Always use horizontal scroll instead of responsive mode |
|
|
72
|
+
| `stickyHeader` | `boolean` | `true` | Stick the header row to the top on scroll |
|
|
73
|
+
| `showHeader` | `boolean` | `true` | Render the header row |
|
|
74
|
+
| `rowExpansion` | `boolean` | `false` | Enable row expansion |
|
|
75
|
+
| `emptyMessage` | `string` | `'No data available'` | Message shown when no rows exist |
|
|
76
|
+
| `trackBy` | `(index: number, row: T) => unknown` | `(index) => index` | Track-by function for row rendering |
|
|
77
|
+
| `virtualization` | `boolean \| 'auto'` | `'auto'` | Virtual scrolling mode |
|
|
78
|
+
| `virtualizationThreshold` | `number` | `200` | Row count threshold for auto-activating virtualization |
|
|
79
|
+
| `rowHeight` | `number` | `48` | Fixed row height in px (required for virtual scrolling) |
|
|
80
|
+
| `overscan` | `number` | `10` | Extra rows rendered above/below the viewport |
|
|
81
|
+
| `breakpoints` | `{ sm: string; md: string }` | `{ sm: '(max-width: 35.999em)', md: '(max-width: 47.999em)' }` | Media query strings for responsive breakpoints |
|
|
82
|
+
|
|
83
|
+
> **Note:** `rowSelection` was previously a `TableConfig` flag but is now a top-level component input alongside the other selection inputs. Selection-related configuration is grouped on the component API for discoverability.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Features
|
|
88
|
+
|
|
89
|
+
- Sortable columns with tri-state cycling (asc → desc → none)
|
|
90
|
+
- **Multi-row selection** with controlled or uncontrolled state
|
|
91
|
+
- **Page-local select-all** header checkbox with `checked` / `unchecked` / `indeterminate` states
|
|
92
|
+
- **Per-row selection-disabled** support via boolean or `(row) => boolean` predicate
|
|
93
|
+
- **Selection column position** — leading (default) or trailing
|
|
94
|
+
- Virtual scrolling for large datasets (auto-activates above threshold)
|
|
95
|
+
- Responsive column hiding by priority or breakpoint
|
|
96
|
+
- Stacked mobile card view with expand/collapse — checkbox surfaces above each card
|
|
97
|
+
- Column resizing via drag handle
|
|
98
|
+
- Column pinning (sticky start/end)
|
|
99
|
+
- Custom cell and header templates via `TemplateRef`
|
|
100
|
+
- Controlled or uncontrolled state for sort, filter, pagination, and selection
|
|
101
|
+
- Loading overlay
|
|
102
|
+
- Empty state message
|
|
103
|
+
- Sticky header
|
|
104
|
+
- Token-driven styling with dark mode support
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Quick Start
|
|
109
|
+
|
|
110
|
+
```html
|
|
111
|
+
<fk-data-table [columns]="columns" [rows]="rows" (rowClick)="onRowClick($event)" />
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Import
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { DataTableComponent } from '@frame-kit/ui-ng';
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
@Component({
|
|
124
|
+
selector: 'app-orders',
|
|
125
|
+
imports: [DataTableComponent],
|
|
126
|
+
templateUrl: './orders.component.html',
|
|
127
|
+
})
|
|
128
|
+
export class OrdersComponent {
|
|
129
|
+
columns: ColumnDef<Order>[] = [
|
|
130
|
+
{ id: 'address', header: 'Address', accessor: 'address', sortable: true },
|
|
131
|
+
{ id: 'status', header: 'Status', accessor: 'status' },
|
|
132
|
+
{ id: 'price', header: 'Price', accessor: 'price', align: 'end' },
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
rows: Order[] = [];
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Selector
|
|
142
|
+
|
|
143
|
+
```html
|
|
144
|
+
<fk-data-table></fk-data-table>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Examples
|
|
150
|
+
|
|
151
|
+
### Sorting
|
|
152
|
+
|
|
153
|
+
```html
|
|
154
|
+
<fk-data-table [columns]="columns" [rows]="rows" (sortChange)="onSort($event)" />
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Mark columns as sortable in the column definition:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
{ id: "price", header: "Price", accessor: "price", sortable: true }
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Controlled sort (server-side)
|
|
164
|
+
|
|
165
|
+
```html
|
|
166
|
+
<fk-data-table [columns]="columns" [rows]="rows" [sortState]="currentSort" (sortChange)="fetchSorted($event)" />
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
When `sortState` is provided, the table delegates sorting to the consumer and does not sort internally.
|
|
170
|
+
|
|
171
|
+
### Row selection — uncontrolled
|
|
172
|
+
|
|
173
|
+
The simplest case. The table manages selection internally; the consumer just opts in.
|
|
174
|
+
|
|
175
|
+
```html
|
|
176
|
+
<fk-data-table [columns]="columns" [rows]="rows" [rowSelection]="true" [getRowId]="getRowId" (selectionChange)="onSelection($event)" />
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
getRowId = (row: Order) => row.id;
|
|
181
|
+
|
|
182
|
+
onSelection(keys: Set<unknown>): void {
|
|
183
|
+
console.log("Selected ids:", [...keys]);
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Row selection — controlled (server-side pagination friendly)
|
|
188
|
+
|
|
189
|
+
When the parent owns the truth, pass `selectedKeys` and update it on every `selectionChange`. Selections persist across pagination because the state lives in the parent.
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
readonly selection = signal<Set<unknown>>(new Set());
|
|
193
|
+
readonly getRowId = (row: Order) => row.id;
|
|
194
|
+
|
|
195
|
+
onSelection(next: Set<unknown>): void {
|
|
196
|
+
this.selection.set(next); // emit a brand-new Set
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
```html
|
|
201
|
+
<fk-data-table [columns]="columns" [rows]="rows" [rowSelection]="true" [selectedKeys]="selection()" [getRowId]="getRowId" (selectionChange)="onSelection($event)" />
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The table never mutates the `Set` you pass in — every selection action emits a brand-new instance.
|
|
205
|
+
|
|
206
|
+
### Row selection — disabled rows
|
|
207
|
+
|
|
208
|
+
Disable selection on specific rows (e.g. archived, locked). The header "select all" automatically skips them.
|
|
209
|
+
|
|
210
|
+
```html
|
|
211
|
+
<fk-data-table [columns]="columns" [rows]="rows" [rowSelection]="true" [getRowId]="getRowId" [selectionDisabled]="isLocked" />
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
isLocked = (row: Order) => row.status === 'Closed';
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`selectionDisabled` accepts a boolean (disable everything) or a `(row) => boolean` predicate. The header still flips to fully checked when **all eligible rows are selected** — disabled rows aren't counted against the all-selected check.
|
|
219
|
+
|
|
220
|
+
### Selection column on the right
|
|
221
|
+
|
|
222
|
+
```html
|
|
223
|
+
<fk-data-table [columns]="columns" [rows]="rows" [rowSelection]="true" [getRowId]="getRowId" selectionColumnPosition="end" />
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Loading
|
|
227
|
+
|
|
228
|
+
```html
|
|
229
|
+
<fk-data-table [columns]="columns" [rows]="rows" [loading]="isLoading" />
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Empty state
|
|
233
|
+
|
|
234
|
+
```html
|
|
235
|
+
<fk-data-table [columns]="columns" [rows]="[]" [config]="{ emptyMessage: 'No records found' }" />
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Virtual scrolling
|
|
239
|
+
|
|
240
|
+
Virtual scrolling activates automatically when row count exceeds `virtualizationThreshold` (default 200). Force it on or off:
|
|
241
|
+
|
|
242
|
+
```html
|
|
243
|
+
<fk-data-table [columns]="columns" [rows]="largeDataset" [config]="{ virtualization: true, rowHeight: 48 }" />
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Responsive mode
|
|
247
|
+
|
|
248
|
+
```html
|
|
249
|
+
<!-- Auto: switches between table and stack based on viewport -->
|
|
250
|
+
<fk-data-table [columns]="columns" [rows]="rows" [config]="{ responsiveMode: 'auto' }" />
|
|
251
|
+
|
|
252
|
+
<!-- Stack: always render mobile cards -->
|
|
253
|
+
<fk-data-table [columns]="columns" [rows]="rows" [config]="{ responsiveMode: 'stack' }" />
|
|
254
|
+
|
|
255
|
+
<!-- Priority: hide lower-priority columns first -->
|
|
256
|
+
<fk-data-table [columns]="columns" [rows]="rows" [config]="{ responsiveMode: 'priority' }" />
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Use `hideBelow` and `priority` on column definitions to control responsive visibility:
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
{ id: "notes", header: "Notes", accessor: "notes", hideBelow: "md", priority: 3 }
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Column resizing
|
|
266
|
+
|
|
267
|
+
```html
|
|
268
|
+
<fk-data-table [columns]="columns" [rows]="rows" (columnResize)="onResize($event)" />
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Enable per column:
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
{ id: "address", header: "Address", accessor: "address", width: "200px", resizable: true }
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Column pinning
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
columns: ColumnDef<Order>[] = [
|
|
281
|
+
{ id: "id", header: "ID", accessor: "id", width: "80px", pinned: "start" },
|
|
282
|
+
{ id: "address", header: "Address", accessor: "address" },
|
|
283
|
+
{ id: "status", header: "Status", accessor: "status" },
|
|
284
|
+
{ id: "actions", header: "", accessor: () => "", width: "60px", pinned: "end" },
|
|
285
|
+
];
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Pinned columns stick to the left or right edge when scrolling horizontally.
|
|
289
|
+
|
|
290
|
+
### Custom cell template
|
|
291
|
+
|
|
292
|
+
```html
|
|
293
|
+
<ng-template #statusCell let-value let-row="row">
|
|
294
|
+
<fk-badge [variant]="row.status === 'active' ? 'success' : 'neutral'"> {{ value }} </fk-badge>
|
|
295
|
+
</ng-template>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
{ id: "status", header: "Status", accessor: "status", cellRenderer: statusCell }
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Accessibility
|
|
305
|
+
|
|
306
|
+
- The standard render path uses semantic `<table>`, `<thead>`, `<tbody>`, `<tr>`, `<th>`, and `<td>` elements
|
|
307
|
+
- The virtual render path uses `role="table"`, `role="rowgroup"`, `role="row"`, `role="columnheader"`, and `role="cell"` on divs
|
|
308
|
+
- Sortable columns announce sort direction via `aria-sort` (`ascending`, `descending`, or unset)
|
|
309
|
+
- `aria-label` is forwarded to the table element
|
|
310
|
+
- Loading overlay uses `aria-live="polite"`
|
|
311
|
+
- Column resize handles are mouse/touch operated and add `cursor: col-resize` during drag
|
|
312
|
+
- **Selection checkboxes** carry an `aria-label` derived from `selectionLabel` (default `"Select row"`); the header checkbox alternates between `"Select all on page"` and `"Deselect all on page"` based on its current state
|
|
313
|
+
- **Indeterminate state** is set programmatically on the header checkbox when some-but-not-all visible rows are selected (standard browser behavior)
|
|
314
|
+
- **Disabled rows** apply the native `disabled` attribute on their checkboxes — keyboard navigation and screen readers treat them correctly
|
|
315
|
+
- **Click event propagation** is stopped on checkbox clicks so `rowClick` only fires for "real" row clicks, not selection toggles
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## Design Tokens
|
|
320
|
+
|
|
321
|
+
`fk-data-table` uses the following design tokens:
|
|
322
|
+
|
|
323
|
+
```scss
|
|
324
|
+
// Head
|
|
325
|
+
--fk-data-table-head-bg
|
|
326
|
+
--fk-data-table-head-color
|
|
327
|
+
--fk-data-table-head-font-weight
|
|
328
|
+
--fk-data-table-head-font-size
|
|
329
|
+
--fk-data-table-head-hover-color
|
|
330
|
+
|
|
331
|
+
// Body
|
|
332
|
+
--fk-data-table-body-bg
|
|
333
|
+
--fk-data-table-body-color
|
|
334
|
+
--fk-data-table-body-font-size
|
|
335
|
+
|
|
336
|
+
// Layout
|
|
337
|
+
--fk-data-table-border-width
|
|
338
|
+
--fk-data-table-border-radius
|
|
339
|
+
--fk-data-table-border-color
|
|
340
|
+
--fk-data-table-cell-padding
|
|
341
|
+
--fk-data-table-row-hover-bg
|
|
342
|
+
--fk-data-table-viewport-height
|
|
343
|
+
|
|
344
|
+
// Empty & Loading
|
|
345
|
+
--fk-data-table-empty-padding
|
|
346
|
+
--fk-data-table-empty-color
|
|
347
|
+
--fk-data-table-loading-bg
|
|
348
|
+
--fk-data-table-loading-color
|
|
349
|
+
|
|
350
|
+
// Resize
|
|
351
|
+
--fk-data-table-resize-handle-width
|
|
352
|
+
--fk-data-table-resize-handle-color
|
|
353
|
+
|
|
354
|
+
// Pinning
|
|
355
|
+
--fk-data-table-pin-offset
|
|
356
|
+
|
|
357
|
+
// Row selection
|
|
358
|
+
--fk-data-table-selection-cell-width
|
|
359
|
+
--fk-data-table-row-selected-bg
|
|
360
|
+
--fk-data-table-row-selected-hover-bg
|
|
361
|
+
--fk-data-table-checkbox-size
|
|
362
|
+
--fk-data-table-checkbox-border-color
|
|
363
|
+
--fk-data-table-checkbox-radius
|
|
364
|
+
--fk-data-table-checkbox-bg
|
|
365
|
+
--fk-data-table-checkbox-hover-border-color
|
|
366
|
+
--fk-data-table-checkbox-checked-bg
|
|
367
|
+
--fk-data-table-checkbox-checked-border-color
|
|
368
|
+
--fk-data-table-checkbox-icon-color
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Customizing Tokens
|
|
372
|
+
|
|
373
|
+
Override tokens in your application's global stylesheet or a scoped selector:
|
|
374
|
+
|
|
375
|
+
```css
|
|
376
|
+
:root {
|
|
377
|
+
--fk-data-table-head-bg: #f1f5f9;
|
|
378
|
+
--fk-data-table-border-color: #e2e8f0;
|
|
379
|
+
--fk-data-table-row-hover-bg: #f8fafc;
|
|
380
|
+
--fk-data-table-row-selected-bg: #dbeafe;
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Or scope overrides to a specific context:
|
|
385
|
+
|
|
386
|
+
```css
|
|
387
|
+
.dark-theme fk-data-table {
|
|
388
|
+
--fk-data-table-head-bg: #1e293b;
|
|
389
|
+
--fk-data-table-head-color: #94a3b8;
|
|
390
|
+
--fk-data-table-body-bg: #0f172a;
|
|
391
|
+
--fk-data-table-body-color: #e2e8f0;
|
|
392
|
+
--fk-data-table-border-color: #334155;
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## Behavior Notes
|
|
399
|
+
|
|
400
|
+
- When `virtualization` is `'auto'`, the table switches to virtual scrolling when `rows.length` exceeds `virtualizationThreshold`.
|
|
401
|
+
- Virtual scrolling requires a fixed `rowHeight`. The standard path does not enforce fixed heights.
|
|
402
|
+
- When `responsiveMode` is `'auto'`, the table switches to a stacked card view on small viewports and a standard table on larger ones.
|
|
403
|
+
- Column resizing is performed outside Angular's zone for performance. The `columnResize` event fires once on drag end.
|
|
404
|
+
- Pinning uses `position: sticky` and requires explicit `width` or `minWidth` on pinned columns for offset calculation.
|
|
405
|
+
- Passing `sortState`, `filterState`, `pageState`, or `selectedKeys` switches the corresponding concern to controlled mode — the table will not manage that state internally.
|
|
406
|
+
- **Selection scope is page-local** — the header checkbox selects/deselects only the rows currently visible on the page, not the entire dataset. Existing selections on other pages persist when the controlled `selectedKeys` is held by the parent.
|
|
407
|
+
- **`getRowId` is required** when `rowSelection` is `true`. Without it, the table falls back to row-reference equality which silently breaks across server-side pagination (each fetch returns brand-new objects). The component logs a `console.error` in dev when this combination is misconfigured.
|
|
408
|
+
- **The `selectedKeys` input is never mutated.** Every selection action returns a brand-new `Set` via `selectionChange`. The parent owns the next state.
|