@surfnet/curve-angular 0.2.0
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/.postcssrc.json +7 -0
- package/.storybook/main.ts +9 -0
- package/.storybook/manager.ts +3 -0
- package/.storybook/preview.ts +28 -0
- package/.storybook/tsconfig.json +8 -0
- package/.turbo/turbo-build.log +32 -0
- package/CHANGELOG.md +29 -0
- package/angular.json +52 -0
- package/components.json +5 -0
- package/dist/fesm2022/surfnet-curve-angular.mjs +4237 -0
- package/dist/fesm2022/surfnet-curve-angular.mjs.map +1 -0
- package/dist/files/geist-cyrillic-ext-wght-italic.woff2 +0 -0
- package/dist/files/geist-cyrillic-ext-wght-normal.woff2 +0 -0
- package/dist/files/geist-cyrillic-wght-italic.woff2 +0 -0
- package/dist/files/geist-cyrillic-wght-normal.woff2 +0 -0
- package/dist/files/geist-latin-ext-wght-italic.woff2 +0 -0
- package/dist/files/geist-latin-ext-wght-normal.woff2 +0 -0
- package/dist/files/geist-latin-wght-italic.woff2 +0 -0
- package/dist/files/geist-latin-wght-normal.woff2 +0 -0
- package/dist/files/geist-vietnamese-wght-italic.woff2 +0 -0
- package/dist/files/geist-vietnamese-wght-normal.woff2 +0 -0
- package/dist/styles.css +2 -0
- package/dist/types/surfnet-curve-angular.d.ts +1017 -0
- package/ng-package.json +20 -0
- package/package.json +73 -0
- package/scripts/copy-font-files.ts +15 -0
- package/scripts/rewrite-helm-imports.ts +60 -0
- package/src/foundations/design-tokens.stories.ts +198 -0
- package/src/lib/ui/avatar/src/index.ts +22 -0
- package/src/lib/ui/avatar/src/lib/hlm-avatar-badge.ts +19 -0
- package/src/lib/ui/avatar/src/lib/hlm-avatar-fallback.ts +20 -0
- package/src/lib/ui/avatar/src/lib/hlm-avatar-group-count.ts +17 -0
- package/src/lib/ui/avatar/src/lib/hlm-avatar-group.ts +17 -0
- package/src/lib/ui/avatar/src/lib/hlm-avatar-image.ts +19 -0
- package/src/lib/ui/avatar/src/lib/hlm-avatar.stories.ts +107 -0
- package/src/lib/ui/avatar/src/lib/hlm-avatar.ts +32 -0
- package/src/lib/ui/breadcrumb/src/index.ts +30 -0
- package/src/lib/ui/breadcrumb/src/lib/hlm-breadcrumb-ellipsis.ts +36 -0
- package/src/lib/ui/breadcrumb/src/lib/hlm-breadcrumb-item.ts +14 -0
- package/src/lib/ui/breadcrumb/src/lib/hlm-breadcrumb-link.ts +36 -0
- package/src/lib/ui/breadcrumb/src/lib/hlm-breadcrumb-list.ts +17 -0
- package/src/lib/ui/breadcrumb/src/lib/hlm-breadcrumb-page.ts +17 -0
- package/src/lib/ui/breadcrumb/src/lib/hlm-breadcrumb-separator.ts +27 -0
- package/src/lib/ui/breadcrumb/src/lib/hlm-breadcrumb.stories.ts +81 -0
- package/src/lib/ui/breadcrumb/src/lib/hlm-breadcrumb.ts +13 -0
- package/src/lib/ui/button/src/index.ts +6 -0
- package/src/lib/ui/button/src/lib/hlm-button.stories.ts +170 -0
- package/src/lib/ui/button/src/lib/hlm-button.token.ts +22 -0
- package/src/lib/ui/button/src/lib/hlm-button.ts +76 -0
- package/src/lib/ui/card/src/index.ts +25 -0
- package/src/lib/ui/card/src/lib/hlm-card-action.ts +12 -0
- package/src/lib/ui/card/src/lib/hlm-card-content.ts +12 -0
- package/src/lib/ui/card/src/lib/hlm-card-description.ts +12 -0
- package/src/lib/ui/card/src/lib/hlm-card-footer.ts +14 -0
- package/src/lib/ui/card/src/lib/hlm-card-header.ts +15 -0
- package/src/lib/ui/card/src/lib/hlm-card-title.ts +12 -0
- package/src/lib/ui/card/src/lib/hlm-card.stories.ts +105 -0
- package/src/lib/ui/card/src/lib/hlm-card.token.ts +19 -0
- package/src/lib/ui/card/src/lib/hlm-card.ts +22 -0
- package/src/lib/ui/checkbox/src/index.ts +5 -0
- package/src/lib/ui/checkbox/src/lib/hlm-checkbox.stories.ts +78 -0
- package/src/lib/ui/checkbox/src/lib/hlm-checkbox.ts +160 -0
- package/src/lib/ui/data-table/src/index.ts +14 -0
- package/src/lib/ui/data-table/src/lib/hlm-data-table-content.ts +87 -0
- package/src/lib/ui/data-table/src/lib/hlm-data-table-pagination.ts +62 -0
- package/src/lib/ui/data-table/src/lib/hlm-data-table-toolbar.ts +16 -0
- package/src/lib/ui/data-table/src/lib/hlm-data-table.stories.ts +479 -0
- package/src/lib/ui/data-table/src/lib/inject-data-table.ts +86 -0
- package/src/lib/ui/dropdown-menu/src/index.ts +45 -0
- package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-checkbox-indicator.ts +20 -0
- package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-checkbox.ts +36 -0
- package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-group.ts +16 -0
- package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-item-sub-indicator.ts +17 -0
- package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-item.ts +41 -0
- package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-label.ts +20 -0
- package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-radio-indicator.ts +20 -0
- package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-radio.ts +36 -0
- package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-separator.ts +14 -0
- package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-shortcut.ts +14 -0
- package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-sub.ts +58 -0
- package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-token.ts +26 -0
- package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu-trigger.ts +49 -0
- package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu.stories.ts +125 -0
- package/src/lib/ui/dropdown-menu/src/lib/hlm-dropdown-menu.ts +62 -0
- package/src/lib/ui/field/src/index.ts +34 -0
- package/src/lib/ui/field/src/lib/hlm-field-content.ts +12 -0
- package/src/lib/ui/field/src/lib/hlm-field-description.ts +53 -0
- package/src/lib/ui/field/src/lib/hlm-field-error.ts +100 -0
- package/src/lib/ui/field/src/lib/hlm-field-group.ts +15 -0
- package/src/lib/ui/field/src/lib/hlm-field-label.ts +17 -0
- package/src/lib/ui/field/src/lib/hlm-field-legend.ts +17 -0
- package/src/lib/ui/field/src/lib/hlm-field-separator.ts +24 -0
- package/src/lib/ui/field/src/lib/hlm-field-set.ts +15 -0
- package/src/lib/ui/field/src/lib/hlm-field-title.ts +15 -0
- package/src/lib/ui/field/src/lib/hlm-field.stories.ts +117 -0
- package/src/lib/ui/field/src/lib/hlm-field.ts +49 -0
- package/src/lib/ui/icon/src/index.ts +7 -0
- package/src/lib/ui/icon/src/lib/hlm-icon.token.ts +20 -0
- package/src/lib/ui/icon/src/lib/hlm-icon.ts +42 -0
- package/src/lib/ui/input/src/index.ts +5 -0
- package/src/lib/ui/input/src/lib/hlm-input.stories.ts +94 -0
- package/src/lib/ui/input/src/lib/hlm-input.ts +20 -0
- package/src/lib/ui/input-group/src/index.ts +22 -0
- package/src/lib/ui/input-group/src/lib/hlm-input-group-addon.ts +40 -0
- package/src/lib/ui/input-group/src/lib/hlm-input-group-button.ts +47 -0
- package/src/lib/ui/input-group/src/lib/hlm-input-group-input.ts +17 -0
- package/src/lib/ui/input-group/src/lib/hlm-input-group-text.ts +14 -0
- package/src/lib/ui/input-group/src/lib/hlm-input-group-textarea.ts +17 -0
- package/src/lib/ui/input-group/src/lib/hlm-input-group.stories.ts +133 -0
- package/src/lib/ui/input-group/src/lib/hlm-input-group.ts +18 -0
- package/src/lib/ui/label/src/index.ts +5 -0
- package/src/lib/ui/label/src/lib/hlm-label.stories.ts +55 -0
- package/src/lib/ui/label/src/lib/hlm-label.ts +17 -0
- package/src/lib/ui/select/src/index.ts +52 -0
- package/src/lib/ui/select/src/lib/hlm-select-content.ts +44 -0
- package/src/lib/ui/select/src/lib/hlm-select-group.ts +14 -0
- package/src/lib/ui/select/src/lib/hlm-select-item.ts +36 -0
- package/src/lib/ui/select/src/lib/hlm-select-label.ts +14 -0
- package/src/lib/ui/select/src/lib/hlm-select-multiple.ts +45 -0
- package/src/lib/ui/select/src/lib/hlm-select-placeholder.ts +17 -0
- package/src/lib/ui/select/src/lib/hlm-select-portal.ts +8 -0
- package/src/lib/ui/select/src/lib/hlm-select-scroll-down.ts +22 -0
- package/src/lib/ui/select/src/lib/hlm-select-scroll-up.ts +22 -0
- package/src/lib/ui/select/src/lib/hlm-select-separator.ts +14 -0
- package/src/lib/ui/select/src/lib/hlm-select-trigger.ts +59 -0
- package/src/lib/ui/select/src/lib/hlm-select-value-template.ts +5 -0
- package/src/lib/ui/select/src/lib/hlm-select-value.ts +18 -0
- package/src/lib/ui/select/src/lib/hlm-select-values-content.ts +9 -0
- package/src/lib/ui/select/src/lib/hlm-select-values.ts +5 -0
- package/src/lib/ui/select/src/lib/hlm-select.stories.ts +105 -0
- package/src/lib/ui/select/src/lib/hlm-select.ts +45 -0
- package/src/lib/ui/separator/src/index.ts +5 -0
- package/src/lib/ui/separator/src/lib/hlm-separator.stories.ts +88 -0
- package/src/lib/ui/separator/src/lib/hlm-separator.ts +19 -0
- package/src/lib/ui/sheet/src/index.ts +34 -0
- package/src/lib/ui/sheet/src/lib/hlm-sheet-close.ts +9 -0
- package/src/lib/ui/sheet/src/lib/hlm-sheet-content.ts +66 -0
- package/src/lib/ui/sheet/src/lib/hlm-sheet-description.ts +14 -0
- package/src/lib/ui/sheet/src/lib/hlm-sheet-footer.ts +12 -0
- package/src/lib/ui/sheet/src/lib/hlm-sheet-header.ts +12 -0
- package/src/lib/ui/sheet/src/lib/hlm-sheet-overlay.ts +27 -0
- package/src/lib/ui/sheet/src/lib/hlm-sheet-portal.ts +8 -0
- package/src/lib/ui/sheet/src/lib/hlm-sheet-title.ts +14 -0
- package/src/lib/ui/sheet/src/lib/hlm-sheet-trigger.ts +9 -0
- package/src/lib/ui/sheet/src/lib/hlm-sheet.ts +29 -0
- package/src/lib/ui/sidebar/src/index.ts +75 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-content.ts +18 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-footer.ts +15 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-group-action.ts +18 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-group-content.ts +15 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-group-label.ts +18 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-group.ts +15 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-header.ts +15 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-input.ts +17 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-inset.ts +15 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu-action.ts +24 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu-badge.ts +18 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu-button.ts +97 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu-item.ts +15 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu-skeleton.ts +33 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu-sub-button.ts +41 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu-sub-item.ts +15 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu-sub.ts +18 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-menu.ts +15 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-rail.ts +34 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-separator.ts +17 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-trigger.ts +35 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar-wrapper.ts +24 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar.service.ts +136 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar.stories.ts +177 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar.token.ts +33 -0
- package/src/lib/ui/sidebar/src/lib/hlm-sidebar.ts +139 -0
- package/src/lib/ui/skeleton/src/index.ts +5 -0
- package/src/lib/ui/skeleton/src/lib/hlm-skeleton.ts +14 -0
- package/src/lib/ui/table/src/index.ts +25 -0
- package/src/lib/ui/table/src/lib/hlm-table.stories.ts +70 -0
- package/src/lib/ui/table/src/lib/hlm-table.ts +129 -0
- package/src/lib/ui/textarea/src/index.ts +5 -0
- package/src/lib/ui/textarea/src/lib/hlm-textarea.stories.ts +72 -0
- package/src/lib/ui/textarea/src/lib/hlm-textarea.ts +21 -0
- package/src/lib/ui/tooltip/src/index.ts +5 -0
- package/src/lib/ui/tooltip/src/lib/hlm-tooltip.ts +44 -0
- package/src/lib/ui/utils/src/index.ts +1 -0
- package/src/lib/ui/utils/src/lib/hlm.ts +313 -0
- package/src/public-api.ts +24 -0
- package/src/styles.css +43 -0
- package/tsconfig.json +45 -0
- package/tsconfig.lib.json +11 -0
- package/tsconfig.lib.prod.json +9 -0
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, computed, input, signal } from '@angular/core';
|
|
2
|
+
import { NgIcon, provideIcons } from '@ng-icons/core';
|
|
3
|
+
import { phosphorArrowsDownUp, phosphorDotsThreeVertical } from '@ng-icons/phosphor-icons/regular';
|
|
4
|
+
import { HlmButton, HlmButtonImports } from '../../../button/src';
|
|
5
|
+
import { HlmCheckbox } from '../../../checkbox/src';
|
|
6
|
+
import { HlmDropdownMenuImports } from '../../../dropdown-menu/src';
|
|
7
|
+
import { HlmInput } from '../../../input/src';
|
|
8
|
+
import { type Meta, type StoryObj } from '@storybook/angular';
|
|
9
|
+
import { dataTableContract } from '@surfnet/curve-contracts';
|
|
10
|
+
import {
|
|
11
|
+
flexRenderComponent,
|
|
12
|
+
type Column,
|
|
13
|
+
type ColumnDef,
|
|
14
|
+
type Row,
|
|
15
|
+
type Table,
|
|
16
|
+
} from '@tanstack/angular-table';
|
|
17
|
+
|
|
18
|
+
import { HlmDataTableContent } from './hlm-data-table-content';
|
|
19
|
+
import { HlmDataTablePagination } from './hlm-data-table-pagination';
|
|
20
|
+
import { HlmDataTableToolbar } from './hlm-data-table-toolbar';
|
|
21
|
+
import { injectDataTable } from './inject-data-table';
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Shared fixtures
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
type Payment = {
|
|
28
|
+
id: string;
|
|
29
|
+
amount: number;
|
|
30
|
+
status: 'pending' | 'processing' | 'success' | 'failed';
|
|
31
|
+
email: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const data: Payment[] = [
|
|
35
|
+
{ id: 'm5gr84i9', amount: 630.44, status: 'success', email: 'michael.mitc@example.com' },
|
|
36
|
+
{ id: '3u1reuv4', amount: 767.5, status: 'success', email: 'felicia.reid@example.com' },
|
|
37
|
+
{ id: 'derv1ws0', amount: 396.84, status: 'processing', email: 'georgia.young@example.com' },
|
|
38
|
+
{ id: '5kma53ae', amount: 475.22, status: 'success', email: 'alma.lawson@example.com' },
|
|
39
|
+
{ id: 'bhqecj4p', amount: 275.43, status: 'failed', email: 'dolores.chambers@example.com' },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const currency = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' });
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Interactive cell components (mounted by FlexRender via flexRenderComponent)
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
@Component({
|
|
49
|
+
selector: 'data-table-select-all',
|
|
50
|
+
imports: [HlmCheckbox],
|
|
51
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
52
|
+
template: `
|
|
53
|
+
<hlm-checkbox
|
|
54
|
+
aria-label="Select all"
|
|
55
|
+
[checked]="table().getIsAllPageRowsSelected()"
|
|
56
|
+
[indeterminate]="table().getIsSomePageRowsSelected()"
|
|
57
|
+
(checkedChange)="table().toggleAllPageRowsSelected($event)"
|
|
58
|
+
/>
|
|
59
|
+
`,
|
|
60
|
+
})
|
|
61
|
+
class DataTableSelectAll {
|
|
62
|
+
public readonly table = input.required<Table<Payment>>();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@Component({
|
|
66
|
+
selector: 'data-table-select-row',
|
|
67
|
+
imports: [HlmCheckbox],
|
|
68
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
69
|
+
template: `
|
|
70
|
+
<hlm-checkbox
|
|
71
|
+
aria-label="Select row"
|
|
72
|
+
[checked]="row().getIsSelected()"
|
|
73
|
+
(checkedChange)="row().toggleSelected($event)"
|
|
74
|
+
/>
|
|
75
|
+
`,
|
|
76
|
+
})
|
|
77
|
+
class DataTableSelectRow {
|
|
78
|
+
public readonly row = input.required<Row<Payment>>();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@Component({
|
|
82
|
+
selector: 'data-table-email-header',
|
|
83
|
+
imports: [HlmButton, NgIcon],
|
|
84
|
+
providers: [provideIcons({ phosphorArrowsDownUp })],
|
|
85
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
86
|
+
template: `
|
|
87
|
+
<button
|
|
88
|
+
hlmBtn
|
|
89
|
+
variant="ghost"
|
|
90
|
+
size="sm"
|
|
91
|
+
(click)="column().toggleSorting(column().getIsSorted() === 'asc')"
|
|
92
|
+
>
|
|
93
|
+
Email
|
|
94
|
+
<ng-icon name="phosphorArrowsDownUp" data-icon="inline-end" />
|
|
95
|
+
</button>
|
|
96
|
+
`,
|
|
97
|
+
})
|
|
98
|
+
class DataTableEmailHeader {
|
|
99
|
+
public readonly column = input.required<Column<Payment>>();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@Component({
|
|
103
|
+
selector: 'data-table-row-actions',
|
|
104
|
+
imports: [HlmButtonImports, HlmDropdownMenuImports, NgIcon],
|
|
105
|
+
providers: [provideIcons({ phosphorDotsThreeVertical })],
|
|
106
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
107
|
+
template: `
|
|
108
|
+
<button
|
|
109
|
+
hlmBtn
|
|
110
|
+
variant="ghost"
|
|
111
|
+
size="icon-sm"
|
|
112
|
+
align="end"
|
|
113
|
+
aria-label="Open menu"
|
|
114
|
+
[hlmDropdownMenuTrigger]="menu"
|
|
115
|
+
>
|
|
116
|
+
<ng-icon name="phosphorDotsThreeVertical" />
|
|
117
|
+
</button>
|
|
118
|
+
<ng-template #menu>
|
|
119
|
+
<hlm-dropdown-menu class="w-48">
|
|
120
|
+
<hlm-dropdown-menu-group>
|
|
121
|
+
<hlm-dropdown-menu-label>Actions</hlm-dropdown-menu-label>
|
|
122
|
+
<button hlmDropdownMenuItem (triggered)="copyId()">Copy payment ID</button>
|
|
123
|
+
</hlm-dropdown-menu-group>
|
|
124
|
+
<hlm-dropdown-menu-separator />
|
|
125
|
+
<button hlmDropdownMenuItem>View customer</button>
|
|
126
|
+
<button hlmDropdownMenuItem>View payment details</button>
|
|
127
|
+
</hlm-dropdown-menu>
|
|
128
|
+
</ng-template>
|
|
129
|
+
`,
|
|
130
|
+
})
|
|
131
|
+
class DataTableRowActions {
|
|
132
|
+
public readonly payment = input.required<Payment>();
|
|
133
|
+
|
|
134
|
+
protected copyId(): void {
|
|
135
|
+
void navigator.clipboard?.writeText(this.payment().id);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
// Presentational cell components (the Angular equivalent of React's JSX cells)
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
@Component({
|
|
144
|
+
selector: 'data-table-status-cell',
|
|
145
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
146
|
+
template: `<span class="capitalize">{{ status() }}</span>`,
|
|
147
|
+
})
|
|
148
|
+
class DataTableStatusCell {
|
|
149
|
+
public readonly status = input.required<string>();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@Component({
|
|
153
|
+
selector: 'data-table-email-cell',
|
|
154
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
155
|
+
template: `<span class="lowercase">{{ email() }}</span>`,
|
|
156
|
+
})
|
|
157
|
+
class DataTableEmailCell {
|
|
158
|
+
public readonly email = input.required<string>();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@Component({
|
|
162
|
+
selector: 'data-table-amount-header',
|
|
163
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
164
|
+
template: `<div class="text-right">Amount</div>`,
|
|
165
|
+
})
|
|
166
|
+
class DataTableAmountHeader {}
|
|
167
|
+
|
|
168
|
+
@Component({
|
|
169
|
+
selector: 'data-table-amount-cell',
|
|
170
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
171
|
+
template: `<div class="text-right font-medium">{{ formatted() }}</div>`,
|
|
172
|
+
})
|
|
173
|
+
class DataTableAmountCell {
|
|
174
|
+
public readonly amount = input.required<number>();
|
|
175
|
+
protected readonly formatted = computed(() => currency.format(this.amount()));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
// Shared columns
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
const columns: ColumnDef<Payment, unknown>[] = [
|
|
183
|
+
{
|
|
184
|
+
id: 'select',
|
|
185
|
+
header: ({ table }) => flexRenderComponent(DataTableSelectAll, { inputs: { table } }),
|
|
186
|
+
cell: ({ row }) => flexRenderComponent(DataTableSelectRow, { inputs: { row } }),
|
|
187
|
+
enableSorting: false,
|
|
188
|
+
enableHiding: false,
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
accessorKey: 'status',
|
|
192
|
+
header: 'Status',
|
|
193
|
+
cell: ({ row }) =>
|
|
194
|
+
flexRenderComponent(DataTableStatusCell, {
|
|
195
|
+
inputs: { status: row.getValue<string>('status') },
|
|
196
|
+
}),
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
accessorKey: 'email',
|
|
200
|
+
header: ({ column }) => flexRenderComponent(DataTableEmailHeader, { inputs: { column } }),
|
|
201
|
+
cell: ({ row }) =>
|
|
202
|
+
flexRenderComponent(DataTableEmailCell, { inputs: { email: row.getValue<string>('email') } }),
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
accessorKey: 'amount',
|
|
206
|
+
header: () => flexRenderComponent(DataTableAmountHeader),
|
|
207
|
+
cell: ({ row }) =>
|
|
208
|
+
flexRenderComponent(DataTableAmountCell, {
|
|
209
|
+
inputs: { amount: row.getValue<number>('amount') },
|
|
210
|
+
}),
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
id: 'actions',
|
|
214
|
+
enableHiding: false,
|
|
215
|
+
cell: ({ row }) =>
|
|
216
|
+
flexRenderComponent(DataTableRowActions, { inputs: { payment: row.original } }),
|
|
217
|
+
},
|
|
218
|
+
];
|
|
219
|
+
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
// Host components (one per story, mirroring the React render functions)
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
|
|
224
|
+
@Component({
|
|
225
|
+
selector: 'payment-data-table',
|
|
226
|
+
imports: [
|
|
227
|
+
HlmDataTableContent,
|
|
228
|
+
HlmDataTablePagination,
|
|
229
|
+
HlmDataTableToolbar,
|
|
230
|
+
HlmButton,
|
|
231
|
+
HlmInput,
|
|
232
|
+
HlmDropdownMenuImports,
|
|
233
|
+
],
|
|
234
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
235
|
+
host: { class: 'block w-full' },
|
|
236
|
+
template: `
|
|
237
|
+
<div hlmDataTableToolbar>
|
|
238
|
+
<input
|
|
239
|
+
hlmInput
|
|
240
|
+
class="max-w-sm"
|
|
241
|
+
placeholder="Filter emails…"
|
|
242
|
+
[value]="emailFilter()"
|
|
243
|
+
(input)="setEmailFilter($event)"
|
|
244
|
+
/>
|
|
245
|
+
<button hlmBtn variant="outline" class="ml-auto" [hlmDropdownMenuTrigger]="columnsMenu">
|
|
246
|
+
Columns
|
|
247
|
+
</button>
|
|
248
|
+
<ng-template #columnsMenu>
|
|
249
|
+
<hlm-dropdown-menu class="w-40">
|
|
250
|
+
@for (column of hideableColumns(); track column.id) {
|
|
251
|
+
<button
|
|
252
|
+
hlmDropdownMenuCheckbox
|
|
253
|
+
class="capitalize"
|
|
254
|
+
[checked]="column.getIsVisible()"
|
|
255
|
+
(triggered)="column.toggleVisibility(!column.getIsVisible())"
|
|
256
|
+
>
|
|
257
|
+
{{ column.id }}
|
|
258
|
+
<hlm-dropdown-menu-checkbox-indicator />
|
|
259
|
+
</button>
|
|
260
|
+
}
|
|
261
|
+
</hlm-dropdown-menu>
|
|
262
|
+
</ng-template>
|
|
263
|
+
</div>
|
|
264
|
+
<hlm-data-table-content [table]="table" [columns]="columns" />
|
|
265
|
+
<hlm-data-table-pagination [table]="table" />
|
|
266
|
+
`,
|
|
267
|
+
})
|
|
268
|
+
class PaymentDataTable {
|
|
269
|
+
protected readonly _data = signal(data);
|
|
270
|
+
protected readonly columns = columns;
|
|
271
|
+
protected readonly table = injectDataTable(() => ({ data: this._data(), columns: this.columns }));
|
|
272
|
+
|
|
273
|
+
protected readonly emailFilter = signal('');
|
|
274
|
+
|
|
275
|
+
protected hideableColumns() {
|
|
276
|
+
return this.table.getAllColumns().filter((column) => column.getCanHide());
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
protected setEmailFilter(event: Event): void {
|
|
280
|
+
const value = (event.target as HTMLInputElement).value;
|
|
281
|
+
this.emailFilter.set(value);
|
|
282
|
+
this.table.getColumn('email')?.setFilterValue(value);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
@Component({
|
|
287
|
+
selector: 'empty-data-table',
|
|
288
|
+
imports: [HlmDataTableContent],
|
|
289
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
290
|
+
host: { class: 'block w-full' },
|
|
291
|
+
template: `
|
|
292
|
+
<hlm-data-table-content
|
|
293
|
+
[table]="table"
|
|
294
|
+
[columns]="columns"
|
|
295
|
+
noResultsLabel="No payments found."
|
|
296
|
+
/>
|
|
297
|
+
`,
|
|
298
|
+
})
|
|
299
|
+
class EmptyDataTable {
|
|
300
|
+
protected readonly columns = columns;
|
|
301
|
+
protected readonly table = injectDataTable<Payment>(() => ({ data: [], columns: this.columns }));
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
@Component({
|
|
305
|
+
selector: 'custom-toolbar-data-table',
|
|
306
|
+
imports: [HlmDataTableContent, HlmDataTablePagination, HlmDataTableToolbar, HlmButton],
|
|
307
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
308
|
+
host: { class: 'block w-full' },
|
|
309
|
+
template: `
|
|
310
|
+
<div hlmDataTableToolbar class="justify-between">
|
|
311
|
+
<span class="font-medium">Recent payments</span>
|
|
312
|
+
<button hlmBtn size="sm">Export</button>
|
|
313
|
+
</div>
|
|
314
|
+
<hlm-data-table-content [table]="table" [columns]="columns" />
|
|
315
|
+
<hlm-data-table-pagination [table]="table" />
|
|
316
|
+
`,
|
|
317
|
+
})
|
|
318
|
+
class CustomToolbarDataTable {
|
|
319
|
+
protected readonly _data = signal(data);
|
|
320
|
+
protected readonly columns = columns;
|
|
321
|
+
protected readonly table = injectDataTable(() => ({ data: this._data(), columns: this.columns }));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ---------------------------------------------------------------------------
|
|
325
|
+
// Meta + stories
|
|
326
|
+
// ---------------------------------------------------------------------------
|
|
327
|
+
|
|
328
|
+
const meta: Meta = {
|
|
329
|
+
title: 'Components/DataTable',
|
|
330
|
+
parameters: {
|
|
331
|
+
docs: {
|
|
332
|
+
description: {
|
|
333
|
+
component: dataTableContract.docs.description,
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
export default meta;
|
|
340
|
+
type Story = StoryObj;
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Full table: filter input, column-visibility toggle, row selection, sortable Email column,
|
|
344
|
+
* right-aligned Amount, row actions, and Previous/Next pagination.
|
|
345
|
+
*/
|
|
346
|
+
export const Default: Story = {
|
|
347
|
+
parameters: {
|
|
348
|
+
docs: {
|
|
349
|
+
source: {
|
|
350
|
+
language: 'typescript',
|
|
351
|
+
code: `@Component({
|
|
352
|
+
selector: 'payment-data-table',
|
|
353
|
+
imports: [
|
|
354
|
+
HlmDataTableContent,
|
|
355
|
+
HlmDataTablePagination,
|
|
356
|
+
HlmDataTableToolbar,
|
|
357
|
+
HlmButton,
|
|
358
|
+
HlmInput,
|
|
359
|
+
HlmDropdownMenuImports,
|
|
360
|
+
],
|
|
361
|
+
host: { class: 'block w-full' },
|
|
362
|
+
template: \`
|
|
363
|
+
<div hlmDataTableToolbar>
|
|
364
|
+
<input
|
|
365
|
+
hlmInput
|
|
366
|
+
class="max-w-sm"
|
|
367
|
+
placeholder="Filter emails…"
|
|
368
|
+
[value]="emailFilter()"
|
|
369
|
+
(input)="setEmailFilter($event)"
|
|
370
|
+
/>
|
|
371
|
+
<button hlmBtn variant="outline" class="ml-auto" [hlmDropdownMenuTrigger]="columnsMenu">
|
|
372
|
+
Columns
|
|
373
|
+
</button>
|
|
374
|
+
<ng-template #columnsMenu>
|
|
375
|
+
<hlm-dropdown-menu class="w-40">
|
|
376
|
+
@for (column of hideableColumns(); track column.id) {
|
|
377
|
+
<button
|
|
378
|
+
hlmDropdownMenuCheckbox
|
|
379
|
+
class="capitalize"
|
|
380
|
+
[checked]="column.getIsVisible()"
|
|
381
|
+
(triggered)="column.toggleVisibility(!column.getIsVisible())"
|
|
382
|
+
>
|
|
383
|
+
{{ column.id }}
|
|
384
|
+
<hlm-dropdown-menu-checkbox-indicator />
|
|
385
|
+
</button>
|
|
386
|
+
}
|
|
387
|
+
</hlm-dropdown-menu>
|
|
388
|
+
</ng-template>
|
|
389
|
+
</div>
|
|
390
|
+
<hlm-data-table-content [table]="table" [columns]="columns" />
|
|
391
|
+
<hlm-data-table-pagination [table]="table" />
|
|
392
|
+
\`,
|
|
393
|
+
})
|
|
394
|
+
class PaymentDataTable {
|
|
395
|
+
protected readonly columns = columns;
|
|
396
|
+
protected readonly table = injectDataTable(() => ({ data, columns: this.columns }));
|
|
397
|
+
protected readonly emailFilter = signal('');
|
|
398
|
+
|
|
399
|
+
protected hideableColumns() {
|
|
400
|
+
return this.table.getAllColumns().filter((column) => column.getCanHide());
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
protected setEmailFilter(event: Event): void {
|
|
404
|
+
const value = (event.target as HTMLInputElement).value;
|
|
405
|
+
this.emailFilter.set(value);
|
|
406
|
+
this.table.getColumn('email')?.setFilterValue(value);
|
|
407
|
+
}
|
|
408
|
+
}`,
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
render: () => ({
|
|
413
|
+
moduleMetadata: { imports: [PaymentDataTable] },
|
|
414
|
+
template: '<payment-data-table />',
|
|
415
|
+
}),
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
/** Empty state — no rows to display. */
|
|
419
|
+
export const Empty: Story = {
|
|
420
|
+
parameters: {
|
|
421
|
+
docs: {
|
|
422
|
+
source: {
|
|
423
|
+
language: 'typescript',
|
|
424
|
+
code: `@Component({
|
|
425
|
+
selector: 'empty-data-table',
|
|
426
|
+
imports: [HlmDataTableContent],
|
|
427
|
+
host: { class: 'block w-full' },
|
|
428
|
+
template: \`
|
|
429
|
+
<hlm-data-table-content
|
|
430
|
+
[table]="table"
|
|
431
|
+
[columns]="columns"
|
|
432
|
+
noResultsLabel="No payments found."
|
|
433
|
+
/>
|
|
434
|
+
\`,
|
|
435
|
+
})
|
|
436
|
+
class EmptyDataTable {
|
|
437
|
+
protected readonly columns = columns;
|
|
438
|
+
protected readonly table = injectDataTable<Payment>(() => ({ data: [], columns: this.columns }));
|
|
439
|
+
}`,
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
render: () => ({
|
|
444
|
+
moduleMetadata: { imports: [EmptyDataTable] },
|
|
445
|
+
template: '<empty-data-table />',
|
|
446
|
+
}),
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
/** Custom toolbar layout: heading on the left, action button on the right. */
|
|
450
|
+
export const CustomToolbar: Story = {
|
|
451
|
+
parameters: {
|
|
452
|
+
docs: {
|
|
453
|
+
source: {
|
|
454
|
+
language: 'typescript',
|
|
455
|
+
code: `@Component({
|
|
456
|
+
selector: 'custom-toolbar-data-table',
|
|
457
|
+
imports: [HlmDataTableContent, HlmDataTablePagination, HlmDataTableToolbar, HlmButton],
|
|
458
|
+
host: { class: 'block w-full' },
|
|
459
|
+
template: \`
|
|
460
|
+
<div hlmDataTableToolbar class="justify-between">
|
|
461
|
+
<span class="font-medium">Recent payments</span>
|
|
462
|
+
<button hlmBtn size="sm">Export</button>
|
|
463
|
+
</div>
|
|
464
|
+
<hlm-data-table-content [table]="table" [columns]="columns" />
|
|
465
|
+
<hlm-data-table-pagination [table]="table" />
|
|
466
|
+
\`,
|
|
467
|
+
})
|
|
468
|
+
class CustomToolbarDataTable {
|
|
469
|
+
protected readonly columns = columns;
|
|
470
|
+
protected readonly table = injectDataTable(() => ({ data, columns: this.columns }));
|
|
471
|
+
}`,
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
render: () => ({
|
|
476
|
+
moduleMetadata: { imports: [CustomToolbarDataTable] },
|
|
477
|
+
template: '<custom-toolbar-data-table />',
|
|
478
|
+
}),
|
|
479
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { signal, type WritableSignal } from '@angular/core';
|
|
2
|
+
import {
|
|
3
|
+
createAngularTable,
|
|
4
|
+
getCoreRowModel,
|
|
5
|
+
getFilteredRowModel,
|
|
6
|
+
getPaginationRowModel,
|
|
7
|
+
getSortedRowModel,
|
|
8
|
+
type ColumnDef,
|
|
9
|
+
type ColumnFiltersState,
|
|
10
|
+
type PaginationState,
|
|
11
|
+
type Row,
|
|
12
|
+
type RowSelectionState,
|
|
13
|
+
type SortingState,
|
|
14
|
+
type Table,
|
|
15
|
+
type Updater,
|
|
16
|
+
type VisibilityState,
|
|
17
|
+
} from '@tanstack/angular-table';
|
|
18
|
+
|
|
19
|
+
interface InjectDataTableOptions<TData> {
|
|
20
|
+
data: TData[];
|
|
21
|
+
columns: ColumnDef<TData, unknown>[];
|
|
22
|
+
/** Derive a stable row id (e.g. from a record's id) so row selection survives data changes. */
|
|
23
|
+
getRowId?: (originalRow: TData, index: number, parent?: Row<TData>) => string;
|
|
24
|
+
initialSorting?: SortingState;
|
|
25
|
+
initialColumnFilters?: ColumnFiltersState;
|
|
26
|
+
initialColumnVisibility?: VisibilityState;
|
|
27
|
+
initialPagination?: PaginationState;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Apply a TanStack `Updater` (value or `(old) => new`) to a writable signal. */
|
|
31
|
+
function applyUpdater<T>(state: WritableSignal<T>): (updater: Updater<T>) => void {
|
|
32
|
+
return (updater) =>
|
|
33
|
+
state.set(typeof updater === 'function' ? (updater as (old: T) => T)(state()) : updater);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Angular analog of React's `useDataTable`. Wraps `createAngularTable` with the core,
|
|
38
|
+
* sorted, filtered and paginated row models and manages sorting, column-filter,
|
|
39
|
+
* column-visibility, row-selection and pagination state with signals.
|
|
40
|
+
*
|
|
41
|
+
* Must be called from an injection context (a component constructor or field initializer),
|
|
42
|
+
* since `createAngularTable` injects. Pass an options factory so reactive `data` re-renders
|
|
43
|
+
* the table:
|
|
44
|
+
*
|
|
45
|
+
* ```ts
|
|
46
|
+
* readonly table = injectDataTable(() => ({ data: this.data(), columns }));
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
function injectDataTable<TData>(options: () => InjectDataTableOptions<TData>): Table<TData> {
|
|
50
|
+
const initial = options();
|
|
51
|
+
|
|
52
|
+
const sorting = signal<SortingState>(initial.initialSorting ?? []);
|
|
53
|
+
const columnFilters = signal<ColumnFiltersState>(initial.initialColumnFilters ?? []);
|
|
54
|
+
const columnVisibility = signal<VisibilityState>(initial.initialColumnVisibility ?? {});
|
|
55
|
+
const rowSelection = signal<RowSelectionState>({});
|
|
56
|
+
const pagination = signal<PaginationState>(
|
|
57
|
+
initial.initialPagination ?? { pageIndex: 0, pageSize: 10 },
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
return createAngularTable(() => {
|
|
61
|
+
const { data, columns, getRowId } = options();
|
|
62
|
+
return {
|
|
63
|
+
data,
|
|
64
|
+
columns,
|
|
65
|
+
getRowId,
|
|
66
|
+
state: {
|
|
67
|
+
sorting: sorting(),
|
|
68
|
+
columnFilters: columnFilters(),
|
|
69
|
+
columnVisibility: columnVisibility(),
|
|
70
|
+
rowSelection: rowSelection(),
|
|
71
|
+
pagination: pagination(),
|
|
72
|
+
},
|
|
73
|
+
onSortingChange: applyUpdater(sorting),
|
|
74
|
+
onColumnFiltersChange: applyUpdater(columnFilters),
|
|
75
|
+
onColumnVisibilityChange: applyUpdater(columnVisibility),
|
|
76
|
+
onRowSelectionChange: applyUpdater(rowSelection),
|
|
77
|
+
onPaginationChange: applyUpdater(pagination),
|
|
78
|
+
getCoreRowModel: getCoreRowModel(),
|
|
79
|
+
getSortedRowModel: getSortedRowModel(),
|
|
80
|
+
getFilteredRowModel: getFilteredRowModel(),
|
|
81
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export { injectDataTable, type InjectDataTableOptions };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { HlmDropdownMenu } from './lib/hlm-dropdown-menu';
|
|
2
|
+
import { HlmDropdownMenuCheckbox } from './lib/hlm-dropdown-menu-checkbox';
|
|
3
|
+
import { HlmDropdownMenuCheckboxIndicator } from './lib/hlm-dropdown-menu-checkbox-indicator';
|
|
4
|
+
import { HlmDropdownMenuGroup } from './lib/hlm-dropdown-menu-group';
|
|
5
|
+
import { HlmDropdownMenuItem } from './lib/hlm-dropdown-menu-item';
|
|
6
|
+
import { HlmDropdownMenuItemSubIndicator } from './lib/hlm-dropdown-menu-item-sub-indicator';
|
|
7
|
+
import { HlmDropdownMenuLabel } from './lib/hlm-dropdown-menu-label';
|
|
8
|
+
import { HlmDropdownMenuRadio } from './lib/hlm-dropdown-menu-radio';
|
|
9
|
+
import { HlmDropdownMenuRadioIndicator } from './lib/hlm-dropdown-menu-radio-indicator';
|
|
10
|
+
import { HlmDropdownMenuSeparator } from './lib/hlm-dropdown-menu-separator';
|
|
11
|
+
import { HlmDropdownMenuShortcut } from './lib/hlm-dropdown-menu-shortcut';
|
|
12
|
+
import { HlmDropdownMenuSub } from './lib/hlm-dropdown-menu-sub';
|
|
13
|
+
|
|
14
|
+
import { HlmDropdownMenuTrigger } from './lib/hlm-dropdown-menu-trigger';
|
|
15
|
+
|
|
16
|
+
export * from './lib/hlm-dropdown-menu';
|
|
17
|
+
export * from './lib/hlm-dropdown-menu-checkbox';
|
|
18
|
+
export * from './lib/hlm-dropdown-menu-checkbox-indicator';
|
|
19
|
+
export * from './lib/hlm-dropdown-menu-group';
|
|
20
|
+
export * from './lib/hlm-dropdown-menu-item';
|
|
21
|
+
export * from './lib/hlm-dropdown-menu-item-sub-indicator';
|
|
22
|
+
export * from './lib/hlm-dropdown-menu-label';
|
|
23
|
+
export * from './lib/hlm-dropdown-menu-radio';
|
|
24
|
+
export * from './lib/hlm-dropdown-menu-radio-indicator';
|
|
25
|
+
export * from './lib/hlm-dropdown-menu-separator';
|
|
26
|
+
export * from './lib/hlm-dropdown-menu-shortcut';
|
|
27
|
+
export * from './lib/hlm-dropdown-menu-sub';
|
|
28
|
+
export * from './lib/hlm-dropdown-menu-token';
|
|
29
|
+
export * from './lib/hlm-dropdown-menu-trigger';
|
|
30
|
+
|
|
31
|
+
export const HlmDropdownMenuImports = [
|
|
32
|
+
HlmDropdownMenu,
|
|
33
|
+
HlmDropdownMenuCheckbox,
|
|
34
|
+
HlmDropdownMenuCheckboxIndicator,
|
|
35
|
+
HlmDropdownMenuGroup,
|
|
36
|
+
HlmDropdownMenuItem,
|
|
37
|
+
HlmDropdownMenuItemSubIndicator,
|
|
38
|
+
HlmDropdownMenuLabel,
|
|
39
|
+
HlmDropdownMenuRadio,
|
|
40
|
+
HlmDropdownMenuRadioIndicator,
|
|
41
|
+
HlmDropdownMenuSeparator,
|
|
42
|
+
HlmDropdownMenuShortcut,
|
|
43
|
+
HlmDropdownMenuSub,
|
|
44
|
+
HlmDropdownMenuTrigger,
|
|
45
|
+
] as const;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|
2
|
+
import { NgIcon, provideIcons } from '@ng-icons/core';
|
|
3
|
+
import { phosphorCheck } from '@ng-icons/phosphor-icons/regular';
|
|
4
|
+
import { classes } from '../../../utils/src';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'hlm-dropdown-menu-checkbox-indicator',
|
|
8
|
+
imports: [NgIcon],
|
|
9
|
+
providers: [provideIcons({ phosphorCheck })],
|
|
10
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
11
|
+
template: ` <ng-icon class="text-base" name="phosphorCheck" /> `,
|
|
12
|
+
})
|
|
13
|
+
export class HlmDropdownMenuCheckboxIndicator {
|
|
14
|
+
constructor() {
|
|
15
|
+
classes(
|
|
16
|
+
() =>
|
|
17
|
+
'pointer-events-none absolute left-2 flex size-3.5 items-center justify-center opacity-0 group-data-[checked]:opacity-100',
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type BooleanInput } from '@angular/cdk/coercion';
|
|
2
|
+
import { CdkMenuItemCheckbox } from '@angular/cdk/menu';
|
|
3
|
+
import { Directive, booleanAttribute, inject, input } from '@angular/core';
|
|
4
|
+
import { classes } from '../../../utils/src';
|
|
5
|
+
|
|
6
|
+
@Directive({
|
|
7
|
+
selector: '[hlmDropdownMenuCheckbox]',
|
|
8
|
+
hostDirectives: [
|
|
9
|
+
{
|
|
10
|
+
directive: CdkMenuItemCheckbox,
|
|
11
|
+
inputs: ['cdkMenuItemDisabled: disabled', 'cdkMenuItemChecked: checked'],
|
|
12
|
+
outputs: ['cdkMenuItemTriggered: triggered'],
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
host: {
|
|
16
|
+
'data-slot': 'dropdown-menu-checkbox-item',
|
|
17
|
+
'[attr.data-disabled]': 'disabled() ? "" : null',
|
|
18
|
+
'[attr.data-checked]': 'checked() ? "" : null',
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
export class HlmDropdownMenuCheckbox {
|
|
22
|
+
private readonly _cdkMenuItem = inject(CdkMenuItemCheckbox);
|
|
23
|
+
public readonly checked = input<boolean, BooleanInput>(this._cdkMenuItem.checked, {
|
|
24
|
+
transform: booleanAttribute,
|
|
25
|
+
});
|
|
26
|
+
public readonly disabled = input<boolean, BooleanInput>(this._cdkMenuItem.disabled, {
|
|
27
|
+
transform: booleanAttribute,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
constructor() {
|
|
31
|
+
classes(
|
|
32
|
+
() =>
|
|
33
|
+
'hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground group relative flex w-full cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm transition-colors outline-none select-none has-[>hlm-dropdown-menu-checkbox-indicator:last-child]:ps-2 has-[>hlm-dropdown-menu-checkbox-indicator:last-child]:pe-8 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 has-[>hlm-dropdown-menu-checkbox-indicator:last-child]:[&>hlm-dropdown-menu-checkbox-indicator]:start-auto has-[>hlm-dropdown-menu-checkbox-indicator:last-child]:[&>hlm-dropdown-menu-checkbox-indicator]:end-2',
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CdkMenuGroup } from '@angular/cdk/menu';
|
|
2
|
+
import { Directive } from '@angular/core';
|
|
3
|
+
import { classes } from '../../../utils/src';
|
|
4
|
+
|
|
5
|
+
@Directive({
|
|
6
|
+
selector: '[hlmDropdownMenuGroup],hlm-dropdown-menu-group',
|
|
7
|
+
hostDirectives: [CdkMenuGroup],
|
|
8
|
+
host: {
|
|
9
|
+
'data-slot': 'dropdown-menu-group',
|
|
10
|
+
},
|
|
11
|
+
})
|
|
12
|
+
export class HlmDropdownMenuGroup {
|
|
13
|
+
constructor() {
|
|
14
|
+
classes(() => 'block');
|
|
15
|
+
}
|
|
16
|
+
}
|