@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,100 @@
|
|
|
1
|
+
import { BooleanInput } from '@angular/cdk/coercion';
|
|
2
|
+
import {
|
|
3
|
+
booleanAttribute,
|
|
4
|
+
ChangeDetectionStrategy,
|
|
5
|
+
Component,
|
|
6
|
+
computed,
|
|
7
|
+
effect,
|
|
8
|
+
EffectRef,
|
|
9
|
+
inject,
|
|
10
|
+
input,
|
|
11
|
+
OnDestroy,
|
|
12
|
+
} from '@angular/core';
|
|
13
|
+
import { BrnField, BrnFieldA11yService } from '@spartan-ng/brain/field';
|
|
14
|
+
import { classes } from '../../../utils/src';
|
|
15
|
+
|
|
16
|
+
@Component({
|
|
17
|
+
selector: 'hlm-field-error',
|
|
18
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
19
|
+
host: {
|
|
20
|
+
role: 'alert',
|
|
21
|
+
'data-slot': 'field-error',
|
|
22
|
+
'[attr.id]': 'id()',
|
|
23
|
+
'[hidden]': '!_display()',
|
|
24
|
+
},
|
|
25
|
+
template: `
|
|
26
|
+
@if (_display()) {
|
|
27
|
+
<ng-content />
|
|
28
|
+
}
|
|
29
|
+
`,
|
|
30
|
+
})
|
|
31
|
+
export class HlmFieldError implements OnDestroy {
|
|
32
|
+
private static _id = 0;
|
|
33
|
+
|
|
34
|
+
private readonly _field = inject(BrnField, { optional: true });
|
|
35
|
+
private readonly _a11y = inject(BrnFieldA11yService, { optional: true, host: true });
|
|
36
|
+
|
|
37
|
+
private _registeredId?: string | undefined;
|
|
38
|
+
|
|
39
|
+
private readonly _hasParentField = !!this._field;
|
|
40
|
+
|
|
41
|
+
/** The unique ID for the field error. If none is supplied, it will be auto-generated. */
|
|
42
|
+
public readonly id = input<string>(`hlm-field-error-${HlmFieldError._id++}`);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The name of the specific validator error key to match (e.g. 'required').
|
|
46
|
+
* When omitted, the error is shown if any validation error is present.
|
|
47
|
+
*/
|
|
48
|
+
public readonly validator = input<string>();
|
|
49
|
+
|
|
50
|
+
/** Forces the error message to be visible regardless of the control's validation state. */
|
|
51
|
+
public readonly forceShow = input<boolean, BooleanInput>(false, { transform: booleanAttribute });
|
|
52
|
+
|
|
53
|
+
protected readonly _display = computed(
|
|
54
|
+
() => !this._hasParentField || this.forceShow() || this._hasError(),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
protected readonly _hasError = computed(() => {
|
|
58
|
+
const errors = this._field?.errors();
|
|
59
|
+
if (!errors) return false;
|
|
60
|
+
|
|
61
|
+
const validator = this.validator();
|
|
62
|
+
const spartanInvalid = this._field?.controlState()?.spartanInvalid;
|
|
63
|
+
|
|
64
|
+
if (!spartanInvalid) return false;
|
|
65
|
+
|
|
66
|
+
return validator ? validator in errors : Object.keys(errors).length > 0;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
private readonly _cleanup: EffectRef | null = this._a11y
|
|
70
|
+
? effect(() => {
|
|
71
|
+
const a11y = this._a11y;
|
|
72
|
+
if (!a11y) return;
|
|
73
|
+
|
|
74
|
+
const id = this.id();
|
|
75
|
+
const hasError = this._hasError();
|
|
76
|
+
|
|
77
|
+
if (this._registeredId && (this._registeredId !== id || !hasError)) {
|
|
78
|
+
a11y.unregisterError(this._registeredId);
|
|
79
|
+
this._registeredId = undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (hasError && this._registeredId !== id) {
|
|
83
|
+
a11y.registerError(id);
|
|
84
|
+
this._registeredId = id;
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
: null;
|
|
88
|
+
|
|
89
|
+
constructor() {
|
|
90
|
+
classes(() => 'text-destructive text-sm font-normal');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
ngOnDestroy() {
|
|
94
|
+
this._cleanup?.destroy();
|
|
95
|
+
|
|
96
|
+
if (this._registeredId) {
|
|
97
|
+
this._a11y?.unregisterError(this._registeredId);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Directive } from '@angular/core';
|
|
2
|
+
import { classes } from '../../../utils/src';
|
|
3
|
+
|
|
4
|
+
@Directive({
|
|
5
|
+
selector: '[hlmFieldGroup],hlm-field-group',
|
|
6
|
+
host: { 'data-slot': 'field-group' },
|
|
7
|
+
})
|
|
8
|
+
export class HlmFieldGroup {
|
|
9
|
+
constructor() {
|
|
10
|
+
classes(
|
|
11
|
+
() =>
|
|
12
|
+
'gap-7 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4 group/field-group @container/field-group flex w-full flex-col',
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Directive } from '@angular/core';
|
|
2
|
+
import { HlmLabel } from '../../../label/src';
|
|
3
|
+
import { classes } from '../../../utils/src';
|
|
4
|
+
|
|
5
|
+
@Directive({
|
|
6
|
+
selector: '[hlmFieldLabel],hlm-field-label',
|
|
7
|
+
hostDirectives: [HlmLabel],
|
|
8
|
+
host: { 'data-slot': 'field-label' },
|
|
9
|
+
})
|
|
10
|
+
export class HlmFieldLabel {
|
|
11
|
+
constructor() {
|
|
12
|
+
classes(() => [
|
|
13
|
+
'has-data-checked:bg-primary/5 has-data-checked:border-primary/30 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10 gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border *:data-[slot=field]:p-3 group/field-label peer/field-label flex w-fit',
|
|
14
|
+
'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col',
|
|
15
|
+
]);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Directive, input } from '@angular/core';
|
|
2
|
+
import { classes } from '../../../utils/src';
|
|
3
|
+
|
|
4
|
+
@Directive({
|
|
5
|
+
selector: 'legend[hlmFieldLegend]',
|
|
6
|
+
host: {
|
|
7
|
+
'data-slot': 'field-legend',
|
|
8
|
+
'[attr.data-variant]': 'variant()',
|
|
9
|
+
},
|
|
10
|
+
})
|
|
11
|
+
export class HlmFieldLegend {
|
|
12
|
+
public readonly variant = input<'label' | 'legend'>('legend');
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
classes(() => 'mb-3 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|
2
|
+
import { HlmSeparator } from '../../../separator/src';
|
|
3
|
+
import { classes } from '../../../utils/src';
|
|
4
|
+
|
|
5
|
+
@Component({
|
|
6
|
+
selector: 'hlm-field-separator',
|
|
7
|
+
imports: [HlmSeparator],
|
|
8
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
9
|
+
host: { 'data-slot': 'field-separator' },
|
|
10
|
+
template: `
|
|
11
|
+
<hlm-separator class="absolute inset-0 top-1/2" />
|
|
12
|
+
<span
|
|
13
|
+
data-slot="field-separator-content"
|
|
14
|
+
class="text-muted-foreground px-2 bg-background relative mx-auto block w-fit"
|
|
15
|
+
>
|
|
16
|
+
<ng-content />
|
|
17
|
+
</span>
|
|
18
|
+
`,
|
|
19
|
+
})
|
|
20
|
+
export class HlmFieldSeparator {
|
|
21
|
+
constructor() {
|
|
22
|
+
classes(() => '-my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2 relative');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Directive } from '@angular/core';
|
|
2
|
+
import { classes } from '../../../utils/src';
|
|
3
|
+
|
|
4
|
+
@Directive({
|
|
5
|
+
selector: 'fieldset[hlmFieldSet]',
|
|
6
|
+
host: { 'data-slot': 'field-set' },
|
|
7
|
+
})
|
|
8
|
+
export class HlmFieldSet {
|
|
9
|
+
constructor() {
|
|
10
|
+
classes(
|
|
11
|
+
() =>
|
|
12
|
+
'gap-6 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3 flex flex-col',
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Directive } from '@angular/core';
|
|
2
|
+
import { classes } from '../../../utils/src';
|
|
3
|
+
|
|
4
|
+
@Directive({
|
|
5
|
+
selector: '[hlmFieldTitle],hlm-field-title',
|
|
6
|
+
host: { 'data-slot': 'field-label' },
|
|
7
|
+
})
|
|
8
|
+
export class HlmFieldTitle {
|
|
9
|
+
constructor() {
|
|
10
|
+
classes(
|
|
11
|
+
() =>
|
|
12
|
+
'gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50 flex w-fit items-center',
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { JsonPipe } from '@angular/common';
|
|
2
|
+
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
3
|
+
import { argsToTemplate, type Meta, moduleMetadata, type StoryObj } from '@storybook/angular';
|
|
4
|
+
import { fieldContract } from '@surfnet/curve-contracts';
|
|
5
|
+
import { HlmField, HlmFieldImports } from '..';
|
|
6
|
+
import { HlmCheckbox, HlmInputImports } from '../../../../../public-api';
|
|
7
|
+
|
|
8
|
+
const meta: Meta<HlmField> = {
|
|
9
|
+
title: 'Components/Field',
|
|
10
|
+
component: HlmField,
|
|
11
|
+
decorators: [
|
|
12
|
+
moduleMetadata({
|
|
13
|
+
imports: [HlmFieldImports, HlmInputImports, HlmCheckbox, ReactiveFormsModule, JsonPipe],
|
|
14
|
+
}),
|
|
15
|
+
],
|
|
16
|
+
parameters: {
|
|
17
|
+
docs: {
|
|
18
|
+
description: {
|
|
19
|
+
component: fieldContract.docs.description,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
argTypes: {
|
|
24
|
+
orientation: {
|
|
25
|
+
control: 'inline-radio',
|
|
26
|
+
options: fieldContract.props.orientations,
|
|
27
|
+
description: fieldContract.props.orientations
|
|
28
|
+
.map(
|
|
29
|
+
(orientation) => `\`${orientation}\` — ${fieldContract.docs.orientations[orientation]}`,
|
|
30
|
+
)
|
|
31
|
+
.join('\n\n'),
|
|
32
|
+
table: {
|
|
33
|
+
defaultValue: { summary: fieldContract.defaults.orientations },
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
args: {
|
|
38
|
+
orientation: fieldContract.defaults.orientations,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default meta;
|
|
43
|
+
type Story = StoryObj<HlmField>;
|
|
44
|
+
|
|
45
|
+
/** A label, input, and helper description. */
|
|
46
|
+
export const Default: Story = {
|
|
47
|
+
render: (args) => ({
|
|
48
|
+
props: args,
|
|
49
|
+
template: `
|
|
50
|
+
<div class="w-full max-w-md">
|
|
51
|
+
<div hlmField class="w-72" ${argsToTemplate(args)}>
|
|
52
|
+
<label hlmFieldLabel for="email">Email</label>
|
|
53
|
+
<input hlmInput id="email" type="email" placeholder="you@surf.nl" />
|
|
54
|
+
<p hlmFieldDescription>This is an input description.</p>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
`,
|
|
58
|
+
}),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** A horizontal field, e.g. a checkbox with its label. */
|
|
62
|
+
export const Horizontal: Story = {
|
|
63
|
+
render: () => ({
|
|
64
|
+
template: `
|
|
65
|
+
<div class="w-full max-w-md">
|
|
66
|
+
<div hlmField orientation="horizontal" class="w-72">
|
|
67
|
+
<hlm-checkbox inputId="remember" />
|
|
68
|
+
<label hlmFieldLabel for="remember" class="font-normal">Remember me</label>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
`,
|
|
72
|
+
}),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/** An invalid field showing an error message. Touch the field to test the minimum of 8 characters. */
|
|
76
|
+
export const WithError: Story = {
|
|
77
|
+
render: () => ({
|
|
78
|
+
props: {
|
|
79
|
+
form: new FormGroup({
|
|
80
|
+
password: new FormControl('', {
|
|
81
|
+
validators: [Validators.required, Validators.minLength(8)],
|
|
82
|
+
}),
|
|
83
|
+
}),
|
|
84
|
+
},
|
|
85
|
+
template: `
|
|
86
|
+
<form [formGroup]="form" class="w-full max-w-md">
|
|
87
|
+
<div hlmField class="w-72">
|
|
88
|
+
<label hlmFieldLabel for="password">Password</label>
|
|
89
|
+
<input hlmInput formControlName="password" id="password" type="password" />
|
|
90
|
+
<hlm-field-error validator="required">Password must be at least 8 characters.</hlm-field-error>
|
|
91
|
+
<hlm-field-error validator="minlength">Password must be at least 8 characters.</hlm-field-error>
|
|
92
|
+
</div>
|
|
93
|
+
</form>
|
|
94
|
+
`,
|
|
95
|
+
}),
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/** Several fields grouped, with a labelled separator. */
|
|
99
|
+
export const Group: Story = {
|
|
100
|
+
render: () => ({
|
|
101
|
+
template: `
|
|
102
|
+
<div class="w-full max-w-md">
|
|
103
|
+
<fieldset hlmFieldSet class="w-72">
|
|
104
|
+
<div hlmField>
|
|
105
|
+
<label hlmFieldLabel for="g-email">Email</label>
|
|
106
|
+
<input hlmInput id="g-email" type="email" placeholder="you@surf.nl" />
|
|
107
|
+
</div>
|
|
108
|
+
<hlm-field-separator>Or continue with</hlm-field-separator>
|
|
109
|
+
<div hlmField>
|
|
110
|
+
<label hlmFieldLabel for="g-password">Password</label>
|
|
111
|
+
<input hlmInput id="g-password" type="password" />
|
|
112
|
+
</div>
|
|
113
|
+
</fieldset>
|
|
114
|
+
</div>
|
|
115
|
+
`,
|
|
116
|
+
}),
|
|
117
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Directive, input } from '@angular/core';
|
|
2
|
+
import { BrnField } from '@spartan-ng/brain/field';
|
|
3
|
+
import { classes } from '../../../utils/src';
|
|
4
|
+
import type { FieldOrientationName } from '@surfnet/curve-contracts';
|
|
5
|
+
import { cva, VariantProps } from 'class-variance-authority';
|
|
6
|
+
import type { ClassValue } from 'clsx';
|
|
7
|
+
|
|
8
|
+
const fieldVariants = cva(
|
|
9
|
+
'data-[matches-spartan-invalid=true]:text-destructive gap-3 group/field flex w-full',
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
orientation: {
|
|
13
|
+
vertical: 'flex-col *:w-full [&>.sr-only]:w-auto',
|
|
14
|
+
horizontal: [
|
|
15
|
+
'flex-row items-center',
|
|
16
|
+
'*:data-[slot=field-label]:flex-auto',
|
|
17
|
+
'has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
|
|
18
|
+
],
|
|
19
|
+
responsive: [
|
|
20
|
+
'flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto [&>.sr-only]:w-auto',
|
|
21
|
+
'@md/field-group:*:data-[slot=field-label]:flex-auto',
|
|
22
|
+
'@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
|
|
23
|
+
],
|
|
24
|
+
} satisfies Record<FieldOrientationName, ClassValue>,
|
|
25
|
+
},
|
|
26
|
+
defaultVariants: {
|
|
27
|
+
orientation: 'vertical',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
export type FieldVariants = VariantProps<typeof fieldVariants>;
|
|
33
|
+
|
|
34
|
+
@Directive({
|
|
35
|
+
selector: '[hlmField],hlm-field',
|
|
36
|
+
hostDirectives: [{ directive: BrnField, inputs: ['data-invalid', 'forceInvalid'] }],
|
|
37
|
+
host: {
|
|
38
|
+
role: 'group',
|
|
39
|
+
'data-slot': 'field',
|
|
40
|
+
'[attr.data-orientation]': 'orientation()',
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
export class HlmField {
|
|
44
|
+
public readonly orientation = input<FieldVariants['orientation']>('vertical');
|
|
45
|
+
|
|
46
|
+
constructor() {
|
|
47
|
+
classes(() => fieldVariants({ orientation: this.orientation() }));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { InjectionToken, type ValueProvider, inject } from '@angular/core';
|
|
2
|
+
import type { IconSize } from './hlm-icon';
|
|
3
|
+
|
|
4
|
+
export interface HlmIconConfig {
|
|
5
|
+
size: IconSize;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const defaultConfig: HlmIconConfig = {
|
|
9
|
+
size: 'base',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const HlmIconConfigToken = new InjectionToken<HlmIconConfig>('HlmIconConfig');
|
|
13
|
+
|
|
14
|
+
export function provideHlmIconConfig(config: Partial<HlmIconConfig>): ValueProvider {
|
|
15
|
+
return { provide: HlmIconConfigToken, useValue: { ...defaultConfig, ...config } };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function injectHlmIconConfig(): HlmIconConfig {
|
|
19
|
+
return inject(HlmIconConfigToken, { optional: true }) ?? defaultConfig;
|
|
20
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Directive, computed, input } from '@angular/core';
|
|
2
|
+
import { injectHlmIconConfig } from './hlm-icon.token';
|
|
3
|
+
|
|
4
|
+
export type IconSize =
|
|
5
|
+
| 'xs'
|
|
6
|
+
| 'sm'
|
|
7
|
+
| 'base'
|
|
8
|
+
| 'lg'
|
|
9
|
+
| 'xl'
|
|
10
|
+
| 'none'
|
|
11
|
+
| (Record<never, never> & string);
|
|
12
|
+
|
|
13
|
+
@Directive({
|
|
14
|
+
selector: 'ng-icon[hlmIcon], ng-icon[hlm]',
|
|
15
|
+
host: {
|
|
16
|
+
'[style.--ng-icon__size]': '_computedSize()',
|
|
17
|
+
},
|
|
18
|
+
})
|
|
19
|
+
export class HlmIcon {
|
|
20
|
+
private readonly _config = injectHlmIconConfig();
|
|
21
|
+
public readonly size = input<IconSize>(this._config.size);
|
|
22
|
+
|
|
23
|
+
protected readonly _computedSize = computed(() => {
|
|
24
|
+
const size = this.size();
|
|
25
|
+
|
|
26
|
+
switch (size) {
|
|
27
|
+
case 'xs':
|
|
28
|
+
return '12px';
|
|
29
|
+
case 'sm':
|
|
30
|
+
return '16px';
|
|
31
|
+
case 'base':
|
|
32
|
+
return '24px';
|
|
33
|
+
case 'lg':
|
|
34
|
+
return '32px';
|
|
35
|
+
case 'xl':
|
|
36
|
+
return '48px';
|
|
37
|
+
default: {
|
|
38
|
+
return size;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FormControl,
|
|
3
|
+
FormGroup,
|
|
4
|
+
FormsModule,
|
|
5
|
+
ReactiveFormsModule,
|
|
6
|
+
Validators,
|
|
7
|
+
} from '@angular/forms';
|
|
8
|
+
import { argsToTemplate, type Meta, moduleMetadata, type StoryObj } from '@storybook/angular';
|
|
9
|
+
import { inputContract } from '@surfnet/curve-contracts';
|
|
10
|
+
import { HlmInput, HlmInputImports } from '..';
|
|
11
|
+
import { HlmButton, HlmLabel } from '../../../../../public-api';
|
|
12
|
+
|
|
13
|
+
const meta: Meta<HlmInput> = {
|
|
14
|
+
title: 'Components/Input',
|
|
15
|
+
component: HlmInput,
|
|
16
|
+
decorators: [
|
|
17
|
+
moduleMetadata({
|
|
18
|
+
imports: [HlmInputImports, HlmLabel, HlmButton, FormsModule, ReactiveFormsModule],
|
|
19
|
+
}),
|
|
20
|
+
],
|
|
21
|
+
parameters: {
|
|
22
|
+
docs: {
|
|
23
|
+
description: {
|
|
24
|
+
component: inputContract.docs.description,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
argTypes: {
|
|
29
|
+
type: {
|
|
30
|
+
control: 'select',
|
|
31
|
+
options: ['text', 'email', 'password', 'number', 'search', 'tel', 'url'],
|
|
32
|
+
description: 'The native input type.',
|
|
33
|
+
},
|
|
34
|
+
placeholder: { control: 'text' },
|
|
35
|
+
disabled: { control: 'boolean' },
|
|
36
|
+
},
|
|
37
|
+
args: {
|
|
38
|
+
type: 'text',
|
|
39
|
+
placeholder: 'Type something…',
|
|
40
|
+
disabled: false,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default meta;
|
|
45
|
+
type Story = StoryObj<HlmInput>;
|
|
46
|
+
|
|
47
|
+
/** The default input — tweak it via the controls. */
|
|
48
|
+
export const Default: Story = {
|
|
49
|
+
render: ({ ...args }) => ({
|
|
50
|
+
props: args,
|
|
51
|
+
template: `
|
|
52
|
+
<input hlmInput type="text" class="w-72" ${argsToTemplate(args)} />
|
|
53
|
+
`,
|
|
54
|
+
}),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/** Common input types side by side. */
|
|
58
|
+
export const Types: Story = {
|
|
59
|
+
render: () => ({
|
|
60
|
+
template: `
|
|
61
|
+
<div class="flex w-72 flex-col gap-3">
|
|
62
|
+
<input hlmInput type="text" placeholder="Text" />
|
|
63
|
+
<input hlmInput type="email" placeholder="Email" />
|
|
64
|
+
<input hlmInput type="password" placeholder="Password" />
|
|
65
|
+
<input hlmInput type="search" placeholder="Search" />
|
|
66
|
+
</div>
|
|
67
|
+
`,
|
|
68
|
+
}),
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/** Disabled state. */
|
|
72
|
+
export const Disabled: Story = {
|
|
73
|
+
render: () => ({
|
|
74
|
+
template: `
|
|
75
|
+
<input aria-label="Email" disabled class="w-72" hlmInput placeholder="Disabled"/>
|
|
76
|
+
`,
|
|
77
|
+
}),
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/** Invalid state. Touch the input to test the required validator. */
|
|
81
|
+
export const Error: Story = {
|
|
82
|
+
render: () => ({
|
|
83
|
+
props: {
|
|
84
|
+
form: new FormGroup({
|
|
85
|
+
test: new FormControl('', { validators: [Validators.required] }),
|
|
86
|
+
}),
|
|
87
|
+
},
|
|
88
|
+
template: `
|
|
89
|
+
<form [formGroup]="form" class="w-full max-w-md">
|
|
90
|
+
<input hlmInput formControlName="test" class="w-72" placeholder="Invalid value" />
|
|
91
|
+
</form>
|
|
92
|
+
`,
|
|
93
|
+
}),
|
|
94
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Directive } from '@angular/core';
|
|
2
|
+
import { BrnFieldControlDescribedBy } from '@spartan-ng/brain/field';
|
|
3
|
+
import { BrnInput } from '@spartan-ng/brain/input';
|
|
4
|
+
import { classes } from '../../../utils/src';
|
|
5
|
+
|
|
6
|
+
@Directive({
|
|
7
|
+
selector: '[hlmInput]',
|
|
8
|
+
hostDirectives: [
|
|
9
|
+
{ directive: BrnInput, inputs: ['id', 'forceInvalid'] },
|
|
10
|
+
BrnFieldControlDescribedBy,
|
|
11
|
+
],
|
|
12
|
+
})
|
|
13
|
+
export class HlmInput {
|
|
14
|
+
constructor() {
|
|
15
|
+
classes(
|
|
16
|
+
() =>
|
|
17
|
+
'dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive dark:data-[matches-spartan-invalid=true]:border-destructive/50 h-9 rounded-md border bg-transparent px-2.5 py-1 text-base shadow-xs transition-[color,box-shadow] file:h-7 file:text-sm file:font-medium focus-visible:ring-3 data-[matches-spartan-invalid=true]:ring-3 md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { HlmInputGroup } from './lib/hlm-input-group';
|
|
2
|
+
import { HlmInputGroupAddon } from './lib/hlm-input-group-addon';
|
|
3
|
+
import { HlmInputGroupButton } from './lib/hlm-input-group-button';
|
|
4
|
+
import { HlmInputGroupInput } from './lib/hlm-input-group-input';
|
|
5
|
+
import { HlmInputGroupText } from './lib/hlm-input-group-text';
|
|
6
|
+
import { HlmInputGroupTextarea } from './lib/hlm-input-group-textarea';
|
|
7
|
+
|
|
8
|
+
export * from './lib/hlm-input-group';
|
|
9
|
+
export * from './lib/hlm-input-group-addon';
|
|
10
|
+
export * from './lib/hlm-input-group-button';
|
|
11
|
+
export * from './lib/hlm-input-group-input';
|
|
12
|
+
export * from './lib/hlm-input-group-text';
|
|
13
|
+
export * from './lib/hlm-input-group-textarea';
|
|
14
|
+
|
|
15
|
+
export const HlmInputGroupImports = [
|
|
16
|
+
HlmInputGroup,
|
|
17
|
+
HlmInputGroupAddon,
|
|
18
|
+
HlmInputGroupButton,
|
|
19
|
+
HlmInputGroupInput,
|
|
20
|
+
HlmInputGroupText,
|
|
21
|
+
HlmInputGroupTextarea,
|
|
22
|
+
] as const;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Directive, input } from '@angular/core';
|
|
2
|
+
import { classes } from '../../../utils/src';
|
|
3
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
4
|
+
|
|
5
|
+
const inputGroupAddonVariants = cva(
|
|
6
|
+
"text-muted-foreground h-auto gap-2 py-1.5 text-sm font-medium group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>ng-icon:not([class*='text-'])]:text-[calc(var(--spacing)*4)] flex cursor-text items-center justify-center select-none",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
align: {
|
|
10
|
+
'inline-start': 'ps-2 has-[>button]:-ms-1 has-[>kbd]:ms-[-0.15rem] order-first',
|
|
11
|
+
'inline-end': 'pe-2 has-[>button]:-me-1 has-[>kbd]:me-[-0.15rem] order-last',
|
|
12
|
+
'block-start':
|
|
13
|
+
'px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2 order-first w-full justify-start',
|
|
14
|
+
'block-end':
|
|
15
|
+
'px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2 order-last w-full justify-start',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: {
|
|
19
|
+
align: 'inline-start',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
type InputGroupAddonVariants = VariantProps<typeof inputGroupAddonVariants>;
|
|
25
|
+
|
|
26
|
+
@Directive({
|
|
27
|
+
selector: '[hlmInputGroupAddon],hlm-input-group-addon',
|
|
28
|
+
host: {
|
|
29
|
+
role: 'group',
|
|
30
|
+
'data-slot': 'input-group-addon',
|
|
31
|
+
'[attr.data-align]': 'align()',
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
export class HlmInputGroupAddon {
|
|
35
|
+
public readonly align = input<InputGroupAddonVariants['align']>('inline-start');
|
|
36
|
+
|
|
37
|
+
constructor() {
|
|
38
|
+
classes(() => inputGroupAddonVariants({ align: this.align() }));
|
|
39
|
+
}
|
|
40
|
+
}
|