@shadng/sng-ui 1.0.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/LICENSE +21 -0
- package/README.md +75 -0
- package/cli/sng-ui.js +331 -0
- package/ng-package.json +29 -0
- package/package.json +64 -0
- package/registry.json +72 -0
- package/src/lib/accordion/cn.ts +6 -0
- package/src/lib/accordion/index.ts +18 -0
- package/src/lib/accordion/sng-accordion-content.ts +131 -0
- package/src/lib/accordion/sng-accordion-item.ts +299 -0
- package/src/lib/accordion/sng-accordion-trigger.ts +137 -0
- package/src/lib/accordion/sng-accordion.ts +118 -0
- package/src/lib/accordion/sng-accordion.types.ts +82 -0
- package/src/lib/alert/cn.ts +6 -0
- package/src/lib/alert/index.ts +3 -0
- package/src/lib/alert/sng-alert-description.ts +49 -0
- package/src/lib/alert/sng-alert-title.ts +46 -0
- package/src/lib/alert/sng-alert.ts +48 -0
- package/src/lib/avatar/cn.ts +6 -0
- package/src/lib/avatar/index.ts +3 -0
- package/src/lib/avatar/sng-avatar-fallback.ts +50 -0
- package/src/lib/avatar/sng-avatar-image.ts +73 -0
- package/src/lib/avatar/sng-avatar.ts +60 -0
- package/src/lib/badge/cn.ts +6 -0
- package/src/lib/badge/index.ts +1 -0
- package/src/lib/badge/sng-badge.ts +36 -0
- package/src/lib/breadcrumb/cn.ts +6 -0
- package/src/lib/breadcrumb/index.ts +7 -0
- package/src/lib/breadcrumb/sng-breadcrumb-ellipsis.ts +61 -0
- package/src/lib/breadcrumb/sng-breadcrumb-item.ts +47 -0
- package/src/lib/breadcrumb/sng-breadcrumb-link.ts +43 -0
- package/src/lib/breadcrumb/sng-breadcrumb-list.ts +42 -0
- package/src/lib/breadcrumb/sng-breadcrumb-page.ts +44 -0
- package/src/lib/breadcrumb/sng-breadcrumb-separator.ts +60 -0
- package/src/lib/breadcrumb/sng-breadcrumb.ts +52 -0
- package/src/lib/button/cn.ts +6 -0
- package/src/lib/button/index.ts +2 -0
- package/src/lib/button/sng-button.ts +264 -0
- package/src/lib/calendar/cn.ts +6 -0
- package/src/lib/calendar/index.ts +2 -0
- package/src/lib/calendar/sng-calendar.ts +753 -0
- package/src/lib/card/cn.ts +6 -0
- package/src/lib/card/index.ts +6 -0
- package/src/lib/card/sng-card-content.ts +36 -0
- package/src/lib/card/sng-card-description.ts +38 -0
- package/src/lib/card/sng-card-footer.ts +34 -0
- package/src/lib/card/sng-card-header.ts +34 -0
- package/src/lib/card/sng-card-title.ts +48 -0
- package/src/lib/card/sng-card.ts +43 -0
- package/src/lib/carousel/cn.ts +6 -0
- package/src/lib/carousel/index.ts +18 -0
- package/src/lib/carousel/sng-carousel.ts +526 -0
- package/src/lib/checkbox/cn.ts +6 -0
- package/src/lib/checkbox/index.ts +1 -0
- package/src/lib/checkbox/sng-checkbox.ts +154 -0
- package/src/lib/code-block/cn.ts +6 -0
- package/src/lib/code-block/index.ts +1 -0
- package/src/lib/code-block/sng-code-block.ts +296 -0
- package/src/lib/dialog/cn.ts +6 -0
- package/src/lib/dialog/index.ts +37 -0
- package/src/lib/dialog/sng-dialog-close.ts +76 -0
- package/src/lib/dialog/sng-dialog-content.ts +132 -0
- package/src/lib/dialog/sng-dialog-description.ts +36 -0
- package/src/lib/dialog/sng-dialog-footer.ts +39 -0
- package/src/lib/dialog/sng-dialog-header.ts +39 -0
- package/src/lib/dialog/sng-dialog-title.ts +52 -0
- package/src/lib/dialog/sng-dialog.service.ts +222 -0
- package/src/lib/dialog/sng-dialog.ts +224 -0
- package/src/lib/drawer/cn.ts +6 -0
- package/src/lib/drawer/index.ts +36 -0
- package/src/lib/drawer/sng-drawer-close.ts +28 -0
- package/src/lib/drawer/sng-drawer-content.ts +135 -0
- package/src/lib/drawer/sng-drawer-description.ts +29 -0
- package/src/lib/drawer/sng-drawer-footer.ts +34 -0
- package/src/lib/drawer/sng-drawer-handle.ts +30 -0
- package/src/lib/drawer/sng-drawer-header.ts +30 -0
- package/src/lib/drawer/sng-drawer-title.ts +27 -0
- package/src/lib/drawer/sng-drawer-trigger.ts +21 -0
- package/src/lib/drawer/sng-drawer-wrapper.ts +27 -0
- package/src/lib/drawer/sng-drawer.ts +166 -0
- package/src/lib/file-input/cn.ts +6 -0
- package/src/lib/file-input/index.ts +1 -0
- package/src/lib/file-input/sng-file-input.ts +288 -0
- package/src/lib/hover-card/cn.ts +6 -0
- package/src/lib/hover-card/index.ts +3 -0
- package/src/lib/hover-card/sng-hover-card-content.ts +100 -0
- package/src/lib/hover-card/sng-hover-card-trigger.ts +43 -0
- package/src/lib/hover-card/sng-hover-card.ts +246 -0
- package/src/lib/input/cn.ts +6 -0
- package/src/lib/input/index.ts +1 -0
- package/src/lib/input/sng-input.ts +160 -0
- package/src/lib/layout/cn.ts +6 -0
- package/src/lib/layout/index.ts +98 -0
- package/src/lib/layout/sng-layout-footer.ts +37 -0
- package/src/lib/layout/sng-layout-header.ts +38 -0
- package/src/lib/layout/sng-layout-sidebar-content.ts +149 -0
- package/src/lib/layout/sng-layout-sidebar-footer.ts +54 -0
- package/src/lib/layout/sng-layout-sidebar-group-action.ts +67 -0
- package/src/lib/layout/sng-layout-sidebar-group-content.ts +41 -0
- package/src/lib/layout/sng-layout-sidebar-group-label.ts +53 -0
- package/src/lib/layout/sng-layout-sidebar-group.ts +41 -0
- package/src/lib/layout/sng-layout-sidebar-header.ts +54 -0
- package/src/lib/layout/sng-layout-sidebar-input.ts +112 -0
- package/src/lib/layout/sng-layout-sidebar-inset.ts +45 -0
- package/src/lib/layout/sng-layout-sidebar-menu-action.ts +84 -0
- package/src/lib/layout/sng-layout-sidebar-menu-badge.ts +47 -0
- package/src/lib/layout/sng-layout-sidebar-menu-button.ts +160 -0
- package/src/lib/layout/sng-layout-sidebar-menu-item.ts +40 -0
- package/src/lib/layout/sng-layout-sidebar-menu-skeleton.ts +71 -0
- package/src/lib/layout/sng-layout-sidebar-menu-sub-button.ts +142 -0
- package/src/lib/layout/sng-layout-sidebar-menu-sub-item.ts +38 -0
- package/src/lib/layout/sng-layout-sidebar-menu-sub.ts +48 -0
- package/src/lib/layout/sng-layout-sidebar-menu.ts +41 -0
- package/src/lib/layout/sng-layout-sidebar-provider.ts +189 -0
- package/src/lib/layout/sng-layout-sidebar-rail.ts +60 -0
- package/src/lib/layout/sng-layout-sidebar-separator.ts +38 -0
- package/src/lib/layout/sng-layout-sidebar-trigger.ts +97 -0
- package/src/lib/layout/sng-layout-sidebar.ts +254 -0
- package/src/lib/menu/cn.ts +6 -0
- package/src/lib/menu/index.ts +21 -0
- package/src/lib/menu/sng-context-trigger.ts +128 -0
- package/src/lib/menu/sng-menu-checkbox-item.ts +91 -0
- package/src/lib/menu/sng-menu-item.ts +80 -0
- package/src/lib/menu/sng-menu-label.ts +47 -0
- package/src/lib/menu/sng-menu-radio-group.ts +38 -0
- package/src/lib/menu/sng-menu-radio-item.ts +94 -0
- package/src/lib/menu/sng-menu-separator.ts +27 -0
- package/src/lib/menu/sng-menu-shortcut.ts +25 -0
- package/src/lib/menu/sng-menu-sub-content.ts +267 -0
- package/src/lib/menu/sng-menu-sub-trigger.ts +68 -0
- package/src/lib/menu/sng-menu-sub.ts +124 -0
- package/src/lib/menu/sng-menu-tokens.ts +52 -0
- package/src/lib/menu/sng-menu-trigger.ts +266 -0
- package/src/lib/menu/sng-menu.ts +100 -0
- package/src/lib/nav-menu/cn.ts +6 -0
- package/src/lib/nav-menu/index.ts +6 -0
- package/src/lib/nav-menu/sng-nav-menu-content.ts +72 -0
- package/src/lib/nav-menu/sng-nav-menu-item.ts +109 -0
- package/src/lib/nav-menu/sng-nav-menu-link.ts +54 -0
- package/src/lib/nav-menu/sng-nav-menu-list.ts +43 -0
- package/src/lib/nav-menu/sng-nav-menu-trigger.ts +98 -0
- package/src/lib/nav-menu/sng-nav-menu.ts +99 -0
- package/src/lib/otp-input/cn.ts +6 -0
- package/src/lib/otp-input/index.ts +14 -0
- package/src/lib/otp-input/sng-otp-input-group.ts +38 -0
- package/src/lib/otp-input/sng-otp-input-separator.ts +43 -0
- package/src/lib/otp-input/sng-otp-input-slot.ts +128 -0
- package/src/lib/otp-input/sng-otp-input-tokens.ts +20 -0
- package/src/lib/otp-input/sng-otp-input.ts +301 -0
- package/src/lib/popover/cn.ts +6 -0
- package/src/lib/popover/index.ts +3 -0
- package/src/lib/popover/sng-popover-content.ts +66 -0
- package/src/lib/popover/sng-popover-trigger.ts +44 -0
- package/src/lib/popover/sng-popover.ts +218 -0
- package/src/lib/preview-box/cn.ts +6 -0
- package/src/lib/preview-box/index.ts +5 -0
- package/src/lib/preview-box/sng-code-block.ts +80 -0
- package/src/lib/preview-box/sng-html-block.ts +79 -0
- package/src/lib/preview-box/sng-preview-block.ts +47 -0
- package/src/lib/preview-box/sng-preview-box.ts +369 -0
- package/src/lib/preview-box/sng-style-block.ts +80 -0
- package/src/lib/progress/cn.ts +6 -0
- package/src/lib/progress/index.ts +1 -0
- package/src/lib/progress/sng-progress.ts +65 -0
- package/src/lib/radio/cn.ts +6 -0
- package/src/lib/radio/index.ts +5 -0
- package/src/lib/radio/sng-radio-item.ts +100 -0
- package/src/lib/radio/sng-radio.ts +54 -0
- package/src/lib/resizable/cn.ts +6 -0
- package/src/lib/resizable/index.ts +3 -0
- package/src/lib/resizable/sng-resizable-group.ts +188 -0
- package/src/lib/resizable/sng-resizable-handle.ts +236 -0
- package/src/lib/resizable/sng-resizable-panel.ts +71 -0
- package/src/lib/search-input/cn.ts +6 -0
- package/src/lib/search-input/index.ts +16 -0
- package/src/lib/search-input/sng-search-input-context.ts +24 -0
- package/src/lib/search-input/sng-search-input-empty.ts +42 -0
- package/src/lib/search-input/sng-search-input-group.ts +69 -0
- package/src/lib/search-input/sng-search-input-item.ts +164 -0
- package/src/lib/search-input/sng-search-input-list.ts +34 -0
- package/src/lib/search-input/sng-search-input-separator.ts +32 -0
- package/src/lib/search-input/sng-search-input-shortcut.ts +29 -0
- package/src/lib/search-input/sng-search-input.ts +368 -0
- package/src/lib/select/cn.ts +6 -0
- package/src/lib/select/index.ts +7 -0
- package/src/lib/select/sng-select-content.ts +27 -0
- package/src/lib/select/sng-select-empty.ts +48 -0
- package/src/lib/select/sng-select-group.ts +29 -0
- package/src/lib/select/sng-select-item.ts +140 -0
- package/src/lib/select/sng-select-label.ts +29 -0
- package/src/lib/select/sng-select-separator.ts +29 -0
- package/src/lib/select/sng-select.ts +326 -0
- package/src/lib/separator/cn.ts +6 -0
- package/src/lib/separator/index.ts +1 -0
- package/src/lib/separator/sng-separator.ts +40 -0
- package/src/lib/skeleton/cn.ts +6 -0
- package/src/lib/skeleton/index.ts +1 -0
- package/src/lib/skeleton/sng-skeleton.ts +49 -0
- package/src/lib/slider/cn.ts +6 -0
- package/src/lib/slider/index.ts +2 -0
- package/src/lib/slider/sng-slider.ts +137 -0
- package/src/lib/sng-table/cn.ts +6 -0
- package/src/lib/sng-table/flex-render.ts +222 -0
- package/src/lib/sng-table/index.ts +85 -0
- package/src/lib/sng-table/sng-table-body.ts +59 -0
- package/src/lib/sng-table/sng-table-caption.ts +49 -0
- package/src/lib/sng-table/sng-table-cell.ts +62 -0
- package/src/lib/sng-table/sng-table-footer.ts +60 -0
- package/src/lib/sng-table/sng-table-head.ts +66 -0
- package/src/lib/sng-table/sng-table-header.ts +48 -0
- package/src/lib/sng-table/sng-table-pagination.ts +265 -0
- package/src/lib/sng-table/sng-table-row.ts +65 -0
- package/src/lib/sng-table/sng-table.ts +67 -0
- package/src/lib/sng-table-core/core/create-cell.ts +117 -0
- package/src/lib/sng-table-core/core/create-column.ts +266 -0
- package/src/lib/sng-table-core/core/create-header.ts +271 -0
- package/src/lib/sng-table-core/core/create-row.ts +293 -0
- package/src/lib/sng-table-core/core/create-table.ts +534 -0
- package/src/lib/sng-table-core/core/types.ts +1197 -0
- package/src/lib/sng-table-core/core/utils.ts +307 -0
- package/src/lib/sng-table-core/features/column-filtering.ts +376 -0
- package/src/lib/sng-table-core/features/column-ordering.ts +159 -0
- package/src/lib/sng-table-core/features/column-pinning.ts +219 -0
- package/src/lib/sng-table-core/features/column-sizing.ts +268 -0
- package/src/lib/sng-table-core/features/column-visibility.ts +128 -0
- package/src/lib/sng-table-core/features/faceting.ts +279 -0
- package/src/lib/sng-table-core/features/fuzzy-filtering.ts +188 -0
- package/src/lib/sng-table-core/features/global-filtering.ts +128 -0
- package/src/lib/sng-table-core/features/pagination.ts +179 -0
- package/src/lib/sng-table-core/features/row-expanding.ts +181 -0
- package/src/lib/sng-table-core/features/row-grouping.ts +235 -0
- package/src/lib/sng-table-core/features/row-pinning.ts +196 -0
- package/src/lib/sng-table-core/features/row-selection.ts +298 -0
- package/src/lib/sng-table-core/features/sorting.ts +425 -0
- package/src/lib/sng-table-core/features/virtualization.ts +298 -0
- package/src/lib/sng-table-core/index.ts +235 -0
- package/src/lib/sng-table-core/row-models/core-row-model.ts +256 -0
- package/src/lib/sng-table-core/row-models/expanded-row-model.ts +175 -0
- package/src/lib/sng-table-core/row-models/filtered-row-model.ts +307 -0
- package/src/lib/sng-table-core/row-models/grouped-row-model.ts +290 -0
- package/src/lib/sng-table-core/row-models/paginated-row-model.ts +135 -0
- package/src/lib/sng-table-core/row-models/sorted-row-model.ts +197 -0
- package/src/lib/styles/sng-themes.css +164 -0
- package/src/lib/switch/cn.ts +6 -0
- package/src/lib/switch/index.ts +1 -0
- package/src/lib/switch/sng-switch.ts +137 -0
- package/src/lib/tabs/cn.ts +6 -0
- package/src/lib/tabs/index.ts +4 -0
- package/src/lib/tabs/sng-tabs-content.ts +66 -0
- package/src/lib/tabs/sng-tabs-list.ts +55 -0
- package/src/lib/tabs/sng-tabs-trigger.ts +86 -0
- package/src/lib/tabs/sng-tabs.ts +83 -0
- package/src/lib/toast/cn.ts +6 -0
- package/src/lib/toast/index.ts +3 -0
- package/src/lib/toast/sng-toast.service.ts +258 -0
- package/src/lib/toast/sng-toast.ts +101 -0
- package/src/lib/toast/sng-toaster.ts +67 -0
- package/src/lib/toggle/cn.ts +6 -0
- package/src/lib/toggle/index.ts +6 -0
- package/src/lib/toggle/sng-toggle-group-item.ts +89 -0
- package/src/lib/toggle/sng-toggle-group.ts +85 -0
- package/src/lib/toggle/sng-toggle.ts +78 -0
- package/src/lib/toggle-group/index.ts +6 -0
- package/src/lib/tooltip/cn.ts +6 -0
- package/src/lib/tooltip/index.ts +5 -0
- package/src/lib/tooltip/sng-tooltip-content.ts +64 -0
- package/src/lib/tooltip/sng-tooltip.ts +216 -0
- package/src/public-api.ts +207 -0
- package/tsconfig.json +24 -0
- package/tsconfig.lib.json +17 -0
- package/tsconfig.lib.prod.json +11 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
computed,
|
|
4
|
+
input,
|
|
5
|
+
model,
|
|
6
|
+
booleanAttribute,
|
|
7
|
+
ChangeDetectionStrategy,
|
|
8
|
+
ViewEncapsulation,
|
|
9
|
+
viewChild,
|
|
10
|
+
ElementRef,
|
|
11
|
+
afterNextRender,
|
|
12
|
+
effect,
|
|
13
|
+
} from '@angular/core';
|
|
14
|
+
import { cn } from './cn';
|
|
15
|
+
|
|
16
|
+
/** Standard input types. */
|
|
17
|
+
export type SngInputType =
|
|
18
|
+
| 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search'
|
|
19
|
+
| 'date' | 'time' | 'datetime-local';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Standard input component for text, email, password, number, and other basic input types.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```html
|
|
26
|
+
* <sng-input placeholder="Enter your email" type="email" />
|
|
27
|
+
* <sng-input type="password" [(value)]="password" />
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
@Component({
|
|
31
|
+
selector: 'sng-input',
|
|
32
|
+
standalone: true,
|
|
33
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
34
|
+
encapsulation: ViewEncapsulation.None,
|
|
35
|
+
host: {
|
|
36
|
+
'class': 'contents',
|
|
37
|
+
'[attr.id]': 'null',
|
|
38
|
+
'[attr.name]': 'null',
|
|
39
|
+
},
|
|
40
|
+
template: `
|
|
41
|
+
<input
|
|
42
|
+
#inputRef
|
|
43
|
+
[type]="type()"
|
|
44
|
+
[attr.id]="id()"
|
|
45
|
+
[attr.name]="name()"
|
|
46
|
+
[placeholder]="placeholder()"
|
|
47
|
+
[disabled]="disabled()"
|
|
48
|
+
[readonly]="readonly()"
|
|
49
|
+
[required]="required()"
|
|
50
|
+
[attr.min]="min()"
|
|
51
|
+
[attr.max]="max()"
|
|
52
|
+
[attr.minlength]="minlength()"
|
|
53
|
+
[attr.maxlength]="maxlength()"
|
|
54
|
+
[attr.pattern]="pattern()"
|
|
55
|
+
[attr.step]="step()"
|
|
56
|
+
[attr.autocomplete]="autocomplete()"
|
|
57
|
+
[value]="value()"
|
|
58
|
+
[class]="inputClasses()"
|
|
59
|
+
(input)="onInput($event)"
|
|
60
|
+
/>
|
|
61
|
+
`,
|
|
62
|
+
})
|
|
63
|
+
export class SngInput {
|
|
64
|
+
private inputRef = viewChild<ElementRef<HTMLInputElement>>('inputRef');
|
|
65
|
+
private hasAppliedAutofocus = false;
|
|
66
|
+
|
|
67
|
+
/** Input type. */
|
|
68
|
+
type = input<SngInputType>('text');
|
|
69
|
+
|
|
70
|
+
/** Input id attribute. */
|
|
71
|
+
id = input<string>();
|
|
72
|
+
|
|
73
|
+
/** Input name attribute. */
|
|
74
|
+
name = input<string>();
|
|
75
|
+
|
|
76
|
+
/** Placeholder text. */
|
|
77
|
+
placeholder = input<string>('');
|
|
78
|
+
|
|
79
|
+
/** Whether the input is disabled. */
|
|
80
|
+
disabled = input(false, { transform: booleanAttribute });
|
|
81
|
+
|
|
82
|
+
/** Whether the input is readonly. */
|
|
83
|
+
readonly = input(false, { transform: booleanAttribute });
|
|
84
|
+
|
|
85
|
+
/** Whether the input is required. */
|
|
86
|
+
required = input(false, { transform: booleanAttribute });
|
|
87
|
+
|
|
88
|
+
/** Custom CSS classes. */
|
|
89
|
+
class = input<string>('');
|
|
90
|
+
|
|
91
|
+
/** Current value. Supports two-way binding via [(value)]. */
|
|
92
|
+
value = model<string | number>('');
|
|
93
|
+
|
|
94
|
+
/** Minimum value (for number/date inputs). */
|
|
95
|
+
min = input<string | number>();
|
|
96
|
+
|
|
97
|
+
/** Maximum value (for number/date inputs). */
|
|
98
|
+
max = input<string | number>();
|
|
99
|
+
|
|
100
|
+
/** Minimum length. */
|
|
101
|
+
minlength = input<number>();
|
|
102
|
+
|
|
103
|
+
/** Maximum length. */
|
|
104
|
+
maxlength = input<number>();
|
|
105
|
+
|
|
106
|
+
/** Pattern for validation. */
|
|
107
|
+
pattern = input<string>();
|
|
108
|
+
|
|
109
|
+
/** Step value (for number inputs). */
|
|
110
|
+
step = input<string | number>();
|
|
111
|
+
|
|
112
|
+
/** Autocomplete attribute. */
|
|
113
|
+
autocomplete = input<string>();
|
|
114
|
+
|
|
115
|
+
/** Whether to autofocus. */
|
|
116
|
+
autofocus = input(false, { transform: booleanAttribute });
|
|
117
|
+
|
|
118
|
+
constructor() {
|
|
119
|
+
effect(() => {
|
|
120
|
+
if (!this.autofocus() || this.hasAppliedAutofocus) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
afterNextRender(() => {
|
|
124
|
+
this.inputRef()?.nativeElement.focus();
|
|
125
|
+
this.hasAppliedAutofocus = true;
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** @internal */
|
|
131
|
+
inputClasses = computed(() =>
|
|
132
|
+
cn(
|
|
133
|
+
'flex w-full min-w-0 rounded-md border border-input bg-transparent',
|
|
134
|
+
'h-9 text-sm px-3 py-1',
|
|
135
|
+
'shadow-xs transition-[color,box-shadow] outline-none',
|
|
136
|
+
'placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground',
|
|
137
|
+
'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',
|
|
138
|
+
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
|
|
139
|
+
'dark:bg-input/30',
|
|
140
|
+
'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
141
|
+
this.class()
|
|
142
|
+
)
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
/** @internal */
|
|
146
|
+
onInput(event: Event): void {
|
|
147
|
+
const value = (event.target as HTMLInputElement).value;
|
|
148
|
+
this.value.set(value);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Focus the input element. */
|
|
152
|
+
focus(): void {
|
|
153
|
+
this.inputRef()?.nativeElement.focus();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Blur the input element. */
|
|
157
|
+
blur(): void {
|
|
158
|
+
this.inputRef()?.nativeElement.blur();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview sng-layout components
|
|
3
|
+
*
|
|
4
|
+
* Layout components for building page structures with header, footer, and sidebar.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import {
|
|
9
|
+
* SngLayoutHeader,
|
|
10
|
+
* SngLayoutFooter,
|
|
11
|
+
* SngLayoutSidebarProvider,
|
|
12
|
+
* SngLayoutSidebar,
|
|
13
|
+
* SngLayoutSidebarContent,
|
|
14
|
+
* SngLayoutSidebarMenu,
|
|
15
|
+
* SngLayoutSidebarMenuItem,
|
|
16
|
+
* SngLayoutSidebarMenuButton,
|
|
17
|
+
* SngLayoutSidebarInset,
|
|
18
|
+
* } from 'sng-ui';
|
|
19
|
+
*
|
|
20
|
+
* @Component({
|
|
21
|
+
* imports: [
|
|
22
|
+
* SngLayoutHeader,
|
|
23
|
+
* SngLayoutFooter,
|
|
24
|
+
* SngLayoutSidebarProvider,
|
|
25
|
+
* SngLayoutSidebar,
|
|
26
|
+
* SngLayoutSidebarContent,
|
|
27
|
+
* SngLayoutSidebarMenu,
|
|
28
|
+
* SngLayoutSidebarMenuItem,
|
|
29
|
+
* SngLayoutSidebarMenuButton,
|
|
30
|
+
* SngLayoutSidebarInset,
|
|
31
|
+
* ],
|
|
32
|
+
* template: `
|
|
33
|
+
* <sng-layout-sidebar-provider>
|
|
34
|
+
* <sng-layout-sidebar>
|
|
35
|
+
* <sng-layout-sidebar-content>
|
|
36
|
+
* <sng-layout-sidebar-menu>
|
|
37
|
+
* <sng-layout-sidebar-menu-item>
|
|
38
|
+
* <sng-layout-sidebar-menu-button routerLink="/home">Home</sng-layout-sidebar-menu-button>
|
|
39
|
+
* </sng-layout-sidebar-menu-item>
|
|
40
|
+
* </sng-layout-sidebar-menu>
|
|
41
|
+
* </sng-layout-sidebar-content>
|
|
42
|
+
* </sng-layout-sidebar>
|
|
43
|
+
* <sng-layout-sidebar-inset>
|
|
44
|
+
* <sng-layout-header>Header content</sng-layout-header>
|
|
45
|
+
* <main>Main content</main>
|
|
46
|
+
* <sng-layout-footer>Footer content</sng-layout-footer>
|
|
47
|
+
* </sng-layout-sidebar-inset>
|
|
48
|
+
* </sng-layout-sidebar-provider>
|
|
49
|
+
* `
|
|
50
|
+
* })
|
|
51
|
+
* export class MyLayoutComponent { }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
// Header & Footer (can be used standalone)
|
|
56
|
+
export { SngLayoutHeader } from './sng-layout-header';
|
|
57
|
+
export { SngLayoutFooter } from './sng-layout-footer';
|
|
58
|
+
|
|
59
|
+
// Sidebar Provider & Context
|
|
60
|
+
export {
|
|
61
|
+
SngLayoutSidebarProvider,
|
|
62
|
+
SNG_LAYOUT_SIDEBAR_CONTEXT,
|
|
63
|
+
type LayoutSidebarContext,
|
|
64
|
+
type LayoutSidebarDirection,
|
|
65
|
+
} from './sng-layout-sidebar-provider';
|
|
66
|
+
|
|
67
|
+
// Sidebar Main Components
|
|
68
|
+
export {
|
|
69
|
+
SngLayoutSidebar,
|
|
70
|
+
type LayoutSidebarSide,
|
|
71
|
+
type LayoutSidebarCollapsible,
|
|
72
|
+
type LayoutSidebarLayout,
|
|
73
|
+
} from './sng-layout-sidebar';
|
|
74
|
+
export { SngLayoutSidebarHeader } from './sng-layout-sidebar-header';
|
|
75
|
+
export { SngLayoutSidebarContent } from './sng-layout-sidebar-content';
|
|
76
|
+
export { SngLayoutSidebarFooter } from './sng-layout-sidebar-footer';
|
|
77
|
+
export { SngLayoutSidebarInset } from './sng-layout-sidebar-inset';
|
|
78
|
+
export { SngLayoutSidebarTrigger } from './sng-layout-sidebar-trigger';
|
|
79
|
+
export { SngLayoutSidebarRail } from './sng-layout-sidebar-rail';
|
|
80
|
+
export { SngLayoutSidebarSeparator } from './sng-layout-sidebar-separator';
|
|
81
|
+
export { SngLayoutSidebarInput } from './sng-layout-sidebar-input';
|
|
82
|
+
|
|
83
|
+
// Sidebar Group Components
|
|
84
|
+
export { SngLayoutSidebarGroup } from './sng-layout-sidebar-group';
|
|
85
|
+
export { SngLayoutSidebarGroupLabel } from './sng-layout-sidebar-group-label';
|
|
86
|
+
export { SngLayoutSidebarGroupContent } from './sng-layout-sidebar-group-content';
|
|
87
|
+
export { SngLayoutSidebarGroupAction } from './sng-layout-sidebar-group-action';
|
|
88
|
+
|
|
89
|
+
// Sidebar Menu Components
|
|
90
|
+
export { SngLayoutSidebarMenu } from './sng-layout-sidebar-menu';
|
|
91
|
+
export { SngLayoutSidebarMenuItem } from './sng-layout-sidebar-menu-item';
|
|
92
|
+
export { SngLayoutSidebarMenuButton, type LayoutSidebarMenuButtonSize } from './sng-layout-sidebar-menu-button';
|
|
93
|
+
export { SngLayoutSidebarMenuSub } from './sng-layout-sidebar-menu-sub';
|
|
94
|
+
export { SngLayoutSidebarMenuSubItem } from './sng-layout-sidebar-menu-sub-item';
|
|
95
|
+
export { SngLayoutSidebarMenuSubButton } from './sng-layout-sidebar-menu-sub-button';
|
|
96
|
+
export { SngLayoutSidebarMenuAction } from './sng-layout-sidebar-menu-action';
|
|
97
|
+
export { SngLayoutSidebarMenuBadge } from './sng-layout-sidebar-menu-badge';
|
|
98
|
+
export { SngLayoutSidebarMenuSkeleton } from './sng-layout-sidebar-menu-skeleton';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
ChangeDetectionStrategy,
|
|
4
|
+
input,
|
|
5
|
+
computed,
|
|
6
|
+
} from '@angular/core';
|
|
7
|
+
import { cn } from './cn';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A flexible footer component for page layouts.
|
|
11
|
+
* Can be used standalone or within a full layout structure.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```html
|
|
15
|
+
* <sng-layout-footer>
|
|
16
|
+
* <span>© 2024 Company</span>
|
|
17
|
+
* <nav>...</nav>
|
|
18
|
+
* </sng-layout-footer>
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
@Component({
|
|
22
|
+
selector: 'sng-layout-footer',
|
|
23
|
+
standalone: true,
|
|
24
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
25
|
+
template: `<ng-content />`,
|
|
26
|
+
host: {
|
|
27
|
+
'[class]': 'hostClasses()',
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
export class SngLayoutFooter {
|
|
31
|
+
/** Custom CSS classes. */
|
|
32
|
+
class = input<string>('');
|
|
33
|
+
|
|
34
|
+
hostClasses = computed(() =>
|
|
35
|
+
cn('flex w-full items-center h-8 px-4 border-t border-border bg-background shrink-0', this.class())
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
ChangeDetectionStrategy,
|
|
4
|
+
input,
|
|
5
|
+
computed,
|
|
6
|
+
} from '@angular/core';
|
|
7
|
+
import { cn } from './cn';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A flexible header component for page layouts.
|
|
11
|
+
* Can be used standalone or within a full layout structure.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```html
|
|
15
|
+
* <sng-layout-header>
|
|
16
|
+
* <img src="logo.svg" alt="Logo" />
|
|
17
|
+
* <nav>...</nav>
|
|
18
|
+
* <button>Login</button>
|
|
19
|
+
* </sng-layout-header>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
@Component({
|
|
23
|
+
selector: 'sng-layout-header',
|
|
24
|
+
standalone: true,
|
|
25
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
26
|
+
template: `<ng-content />`,
|
|
27
|
+
host: {
|
|
28
|
+
'[class]': 'hostClasses()',
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
export class SngLayoutHeader {
|
|
32
|
+
/** Custom CSS classes. */
|
|
33
|
+
class = input<string>('');
|
|
34
|
+
|
|
35
|
+
hostClasses = computed(() =>
|
|
36
|
+
cn('flex w-full items-center h-14 px-4 border-b border-border bg-background shrink-0', this.class())
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
ChangeDetectionStrategy,
|
|
4
|
+
input,
|
|
5
|
+
computed,
|
|
6
|
+
signal,
|
|
7
|
+
OnDestroy,
|
|
8
|
+
} from '@angular/core';
|
|
9
|
+
import { Subscription, timer } from 'rxjs';
|
|
10
|
+
import { cn } from './cn';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Scrollable content area of the sidebar containing navigation groups and menus.
|
|
14
|
+
* Features an auto-hiding scrollbar for a cleaner appearance.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```html
|
|
18
|
+
* <sng-layout-sidebar>
|
|
19
|
+
* <sng-layout-sidebar-header>...</sng-layout-sidebar-header>
|
|
20
|
+
* <sng-layout-sidebar-content [autoHideScrollbar]="2000">
|
|
21
|
+
* <sng-layout-sidebar-group>
|
|
22
|
+
* <sng-layout-sidebar-menu>...</sng-layout-sidebar-menu>
|
|
23
|
+
* </sng-layout-sidebar-group>
|
|
24
|
+
* </sng-layout-sidebar-content>
|
|
25
|
+
* <sng-layout-sidebar-footer>...</sng-layout-sidebar-footer>
|
|
26
|
+
* </sng-layout-sidebar>
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
@Component({
|
|
30
|
+
selector: 'sng-layout-sidebar-content',
|
|
31
|
+
standalone: true,
|
|
32
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
33
|
+
template: `<ng-content />`,
|
|
34
|
+
host: {
|
|
35
|
+
'[class]': 'hostClasses()',
|
|
36
|
+
'[attr.data-scrollbar-visible]': 'scrollbarVisible()',
|
|
37
|
+
'(scroll)': 'onScroll()',
|
|
38
|
+
'(mouseenter)': 'onMouseEnter()',
|
|
39
|
+
'(mouseleave)': 'onMouseLeave()',
|
|
40
|
+
},
|
|
41
|
+
styles: `
|
|
42
|
+
:host {
|
|
43
|
+
/* Thin scrollbar for webkit browsers */
|
|
44
|
+
&::-webkit-scrollbar {
|
|
45
|
+
width: 6px;
|
|
46
|
+
}
|
|
47
|
+
&::-webkit-scrollbar-track {
|
|
48
|
+
background: transparent;
|
|
49
|
+
}
|
|
50
|
+
&::-webkit-scrollbar-thumb {
|
|
51
|
+
background-color: transparent;
|
|
52
|
+
border-radius: 3px;
|
|
53
|
+
transition: background-color 0.3s ease;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Firefox thin scrollbar - hidden by default */
|
|
57
|
+
scrollbar-width: thin;
|
|
58
|
+
scrollbar-color: transparent transparent;
|
|
59
|
+
transition: scrollbar-color 0.3s ease;
|
|
60
|
+
|
|
61
|
+
/* Show scrollbar when visible */
|
|
62
|
+
&[data-scrollbar-visible="true"]::-webkit-scrollbar-thumb {
|
|
63
|
+
background-color: var(--sidebar-border, var(--border));
|
|
64
|
+
}
|
|
65
|
+
&[data-scrollbar-visible="true"]::-webkit-scrollbar-thumb:hover {
|
|
66
|
+
background-color: var(--muted-foreground);
|
|
67
|
+
}
|
|
68
|
+
&[data-scrollbar-visible="true"] {
|
|
69
|
+
scrollbar-color: var(--sidebar-border, var(--border)) transparent;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
`,
|
|
73
|
+
})
|
|
74
|
+
export class SngLayoutSidebarContent implements OnDestroy {
|
|
75
|
+
/** Custom CSS classes. */
|
|
76
|
+
class = input<string>('');
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Auto-hide scrollbar after timeout (in milliseconds). Set to 0 to disable auto-hide.
|
|
80
|
+
*/
|
|
81
|
+
autoHideScrollbar = input<number>(1500);
|
|
82
|
+
|
|
83
|
+
// Scrollbar visibility - computed based on autoHideScrollbar and manual state
|
|
84
|
+
private manualScrollbarVisible = signal(false);
|
|
85
|
+
scrollbarVisible = computed(() => {
|
|
86
|
+
// If auto-hide is disabled (timeout = 0), always show scrollbar
|
|
87
|
+
if (this.autoHideScrollbar() === 0) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
return this.manualScrollbarVisible();
|
|
91
|
+
});
|
|
92
|
+
private hideTimerSubscription: Subscription | null = null;
|
|
93
|
+
private isHovering = signal(false);
|
|
94
|
+
|
|
95
|
+
hostClasses = computed(() =>
|
|
96
|
+
cn(
|
|
97
|
+
'flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden',
|
|
98
|
+
this.class()
|
|
99
|
+
)
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
onScroll() {
|
|
103
|
+
if (this.autoHideScrollbar() > 0) {
|
|
104
|
+
this.showScrollbar();
|
|
105
|
+
this.startHideTimer();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
onMouseEnter() {
|
|
110
|
+
this.isHovering.set(true);
|
|
111
|
+
// Don't show scrollbar on hover - only show when scrolling
|
|
112
|
+
if (this.autoHideScrollbar() > 0) {
|
|
113
|
+
this.clearHideTimer();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
onMouseLeave() {
|
|
118
|
+
this.isHovering.set(false);
|
|
119
|
+
if (this.autoHideScrollbar() > 0 && this.scrollbarVisible()) {
|
|
120
|
+
this.startHideTimer();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private showScrollbar() {
|
|
125
|
+
this.manualScrollbarVisible.set(true);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private startHideTimer() {
|
|
129
|
+
this.clearHideTimer();
|
|
130
|
+
const timeout = this.autoHideScrollbar();
|
|
131
|
+
if (timeout > 0) {
|
|
132
|
+
this.hideTimerSubscription = timer(timeout).subscribe(() => {
|
|
133
|
+
if (!this.isHovering()) {
|
|
134
|
+
this.manualScrollbarVisible.set(false);
|
|
135
|
+
}
|
|
136
|
+
this.hideTimerSubscription = null;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private clearHideTimer() {
|
|
142
|
+
this.hideTimerSubscription?.unsubscribe();
|
|
143
|
+
this.hideTimerSubscription = null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
ngOnDestroy() {
|
|
147
|
+
this.clearHideTimer();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
ChangeDetectionStrategy,
|
|
4
|
+
input,
|
|
5
|
+
computed,
|
|
6
|
+
ViewEncapsulation,
|
|
7
|
+
} from '@angular/core';
|
|
8
|
+
import { cn } from './cn';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Footer section of the sidebar, typically containing user info or actions.
|
|
12
|
+
* Positioned at the bottom of the sidebar.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```html
|
|
16
|
+
* <sng-layout-sidebar>
|
|
17
|
+
* <sng-layout-sidebar-content>...</sng-layout-sidebar-content>
|
|
18
|
+
* <sng-layout-sidebar-footer>
|
|
19
|
+
* <img src="avatar.png" alt="User" />
|
|
20
|
+
* <span>John Doe</span>
|
|
21
|
+
* </sng-layout-sidebar-footer>
|
|
22
|
+
* </sng-layout-sidebar>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
@Component({
|
|
26
|
+
selector: 'sng-layout-sidebar-footer',
|
|
27
|
+
standalone: true,
|
|
28
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
29
|
+
encapsulation: ViewEncapsulation.None,
|
|
30
|
+
styles: [`
|
|
31
|
+
.group[data-collapsible='icon'] sng-layout-sidebar-footer > button {
|
|
32
|
+
width: 2rem !important;
|
|
33
|
+
height: 2rem !important;
|
|
34
|
+
padding: 0 !important;
|
|
35
|
+
}
|
|
36
|
+
`],
|
|
37
|
+
template: `<ng-content />`,
|
|
38
|
+
host: {
|
|
39
|
+
'[class]': 'hostClasses()',
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
export class SngLayoutSidebarFooter {
|
|
43
|
+
/** Custom CSS classes. */
|
|
44
|
+
class = input<string>('');
|
|
45
|
+
|
|
46
|
+
hostClasses = computed(() =>
|
|
47
|
+
cn(
|
|
48
|
+
'flex flex-col gap-2 p-2 overflow-hidden',
|
|
49
|
+
// Icon mode: center content and auto-size buttons
|
|
50
|
+
'group-data-[collapsible=icon]:p-2 group-data-[collapsible=icon]:items-center',
|
|
51
|
+
this.class()
|
|
52
|
+
)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
ChangeDetectionStrategy,
|
|
4
|
+
ViewEncapsulation,
|
|
5
|
+
input,
|
|
6
|
+
computed,
|
|
7
|
+
booleanAttribute,
|
|
8
|
+
} from '@angular/core';
|
|
9
|
+
import { cn } from './cn';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Action button positioned in the top-right corner of a sidebar group.
|
|
13
|
+
* Typically used for adding new items or triggering group-level actions.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```html
|
|
17
|
+
* <sng-layout-sidebar-group>
|
|
18
|
+
* <sng-layout-sidebar-group-label>Projects</sng-layout-sidebar-group-label>
|
|
19
|
+
* <sng-layout-sidebar-group-action (click)="addProject()">
|
|
20
|
+
* <lucide-icon name="plus" />
|
|
21
|
+
* </sng-layout-sidebar-group-action>
|
|
22
|
+
* <sng-layout-sidebar-group-content>...</sng-layout-sidebar-group-content>
|
|
23
|
+
* </sng-layout-sidebar-group>
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
@Component({
|
|
27
|
+
selector: 'sng-layout-sidebar-group-action',
|
|
28
|
+
standalone: true,
|
|
29
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
30
|
+
encapsulation: ViewEncapsulation.None,
|
|
31
|
+
styles: [`
|
|
32
|
+
sng-layout-sidebar-group-action > button > svg {
|
|
33
|
+
width: 1rem;
|
|
34
|
+
height: 1rem;
|
|
35
|
+
flex-shrink: 0;
|
|
36
|
+
}
|
|
37
|
+
`],
|
|
38
|
+
host: {
|
|
39
|
+
'class': 'contents',
|
|
40
|
+
},
|
|
41
|
+
template: `
|
|
42
|
+
<button
|
|
43
|
+
type="button"
|
|
44
|
+
[class]="buttonClasses()"
|
|
45
|
+
[disabled]="disabled()"
|
|
46
|
+
>
|
|
47
|
+
<ng-content />
|
|
48
|
+
</button>
|
|
49
|
+
`,
|
|
50
|
+
})
|
|
51
|
+
export class SngLayoutSidebarGroupAction {
|
|
52
|
+
/** Custom CSS classes. */
|
|
53
|
+
class = input<string>('');
|
|
54
|
+
|
|
55
|
+
/** Whether the button is disabled. */
|
|
56
|
+
disabled = input(false, { transform: booleanAttribute });
|
|
57
|
+
|
|
58
|
+
buttonClasses = computed(() =>
|
|
59
|
+
cn(
|
|
60
|
+
'cursor-pointer text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-colors focus-visible:ring-2',
|
|
61
|
+
// Increases the hit area of the button on mobile
|
|
62
|
+
'after:absolute after:-inset-2 md:after:hidden',
|
|
63
|
+
'group-data-[collapsible=icon]:hidden',
|
|
64
|
+
this.class()
|
|
65
|
+
)
|
|
66
|
+
);
|
|
67
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
ChangeDetectionStrategy,
|
|
4
|
+
input,
|
|
5
|
+
computed,
|
|
6
|
+
} from '@angular/core';
|
|
7
|
+
import { cn } from './cn';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Container for the menu items within a sidebar group.
|
|
11
|
+
* Wraps the menu and applies appropriate spacing.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```html
|
|
15
|
+
* <sng-layout-sidebar-group>
|
|
16
|
+
* <sng-layout-sidebar-group-label>Navigation</sng-layout-sidebar-group-label>
|
|
17
|
+
* <sng-layout-sidebar-group-content>
|
|
18
|
+
* <sng-layout-sidebar-menu>
|
|
19
|
+
* <sng-layout-sidebar-menu-item>...</sng-layout-sidebar-menu-item>
|
|
20
|
+
* </sng-layout-sidebar-menu>
|
|
21
|
+
* </sng-layout-sidebar-group-content>
|
|
22
|
+
* </sng-layout-sidebar-group>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
@Component({
|
|
26
|
+
selector: 'sng-layout-sidebar-group-content',
|
|
27
|
+
standalone: true,
|
|
28
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
29
|
+
template: `<ng-content />`,
|
|
30
|
+
host: {
|
|
31
|
+
'[class]': 'hostClasses()',
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
export class SngLayoutSidebarGroupContent {
|
|
35
|
+
/** Custom CSS classes. */
|
|
36
|
+
class = input<string>('');
|
|
37
|
+
|
|
38
|
+
hostClasses = computed(() =>
|
|
39
|
+
cn('w-full text-sm', this.class())
|
|
40
|
+
);
|
|
41
|
+
}
|