semmet-angular 0.11.0 → 0.12.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.
Files changed (126) hide show
  1. package/README.md +15 -5
  2. package/package.json +1 -1
  3. package/src/badge/files/__name@dasherize__/__name@dasherize__.css.template +102 -0
  4. package/src/badge/files/__name@dasherize__/__name@dasherize__.html.template +14 -0
  5. package/src/badge/files/__name@dasherize__/__name@dasherize__.ts.template +43 -0
  6. package/src/badge/index.d.ts +1 -0
  7. package/src/badge/index.js +6 -0
  8. package/src/badge/index.js.map +1 -0
  9. package/src/badge/index.ts +3 -0
  10. package/src/badge/schema.d.ts +1 -0
  11. package/src/badge/schema.js +3 -0
  12. package/src/badge/schema.js.map +1 -0
  13. package/src/badge/schema.json +29 -0
  14. package/src/badge/schema.ts +1 -0
  15. package/src/button-group/files/__name@dasherize__/__name@dasherize__.css.template +93 -0
  16. package/src/button-group/files/__name@dasherize__/__name@dasherize__.html.template +20 -0
  17. package/src/button-group/files/__name@dasherize__/__name@dasherize__.ts.template +85 -0
  18. package/src/button-group/index.d.ts +1 -0
  19. package/src/button-group/index.js +6 -0
  20. package/src/button-group/index.js.map +1 -0
  21. package/src/button-group/index.ts +3 -0
  22. package/src/button-group/schema.d.ts +1 -0
  23. package/src/button-group/schema.js +3 -0
  24. package/src/button-group/schema.js.map +1 -0
  25. package/src/button-group/schema.json +29 -0
  26. package/src/button-group/schema.ts +1 -0
  27. package/src/close-button/files/__name@dasherize__/__name@dasherize__.css.template +105 -0
  28. package/src/close-button/files/__name@dasherize__/__name@dasherize__.html.template +11 -0
  29. package/src/close-button/files/__name@dasherize__/__name@dasherize__.ts.template +38 -0
  30. package/src/close-button/index.d.ts +1 -0
  31. package/src/close-button/index.js +6 -0
  32. package/src/close-button/index.js.map +1 -0
  33. package/src/close-button/index.ts +3 -0
  34. package/src/close-button/schema.d.ts +1 -0
  35. package/src/close-button/schema.js +3 -0
  36. package/src/close-button/schema.js.map +1 -0
  37. package/src/close-button/schema.json +29 -0
  38. package/src/close-button/schema.ts +1 -0
  39. package/src/collection.json +50 -0
  40. package/src/input-group/files/__name@dasherize__/__name@dasherize__.css.template +119 -0
  41. package/src/input-group/files/__name@dasherize__/__name@dasherize__.html.template +51 -0
  42. package/src/input-group/files/__name@dasherize__/__name@dasherize__.ts.template +64 -0
  43. package/src/input-group/index.d.ts +1 -0
  44. package/src/input-group/index.js +6 -0
  45. package/src/input-group/index.js.map +1 -0
  46. package/src/input-group/index.ts +3 -0
  47. package/src/input-group/schema.d.ts +1 -0
  48. package/src/input-group/schema.js +3 -0
  49. package/src/input-group/schema.js.map +1 -0
  50. package/src/input-group/schema.json +29 -0
  51. package/src/input-group/schema.ts +1 -0
  52. package/src/list-group/files/__name@dasherize__/__name@dasherize__.css.template +121 -0
  53. package/src/list-group/files/__name@dasherize__/__name@dasherize__.html.template +47 -0
  54. package/src/list-group/files/__name@dasherize__/__name@dasherize__.ts.template +55 -0
  55. package/src/list-group/index.d.ts +1 -0
  56. package/src/list-group/index.js +6 -0
  57. package/src/list-group/index.js.map +1 -0
  58. package/src/list-group/index.ts +3 -0
  59. package/src/list-group/schema.d.ts +1 -0
  60. package/src/list-group/schema.js +3 -0
  61. package/src/list-group/schema.js.map +1 -0
  62. package/src/list-group/schema.json +29 -0
  63. package/src/list-group/schema.ts +1 -0
  64. package/src/navbar/files/__name@dasherize__/__name@dasherize__.css.template +158 -0
  65. package/src/navbar/files/__name@dasherize__/__name@dasherize__.html.template +36 -0
  66. package/src/navbar/files/__name@dasherize__/__name@dasherize__.ts.template +47 -0
  67. package/src/navbar/index.d.ts +1 -0
  68. package/src/navbar/index.js +6 -0
  69. package/src/navbar/index.js.map +1 -0
  70. package/src/navbar/index.ts +3 -0
  71. package/src/navbar/schema.d.ts +1 -0
  72. package/src/navbar/schema.js +3 -0
  73. package/src/navbar/schema.js.map +1 -0
  74. package/src/navbar/schema.json +29 -0
  75. package/src/navbar/schema.ts +1 -0
  76. package/src/offcanvas/files/__name@dasherize__/__name@dasherize__.css.template +180 -0
  77. package/src/offcanvas/files/__name@dasherize__/__name@dasherize__.html.template +52 -0
  78. package/src/offcanvas/files/__name@dasherize__/__name@dasherize__.ts.template +96 -0
  79. package/src/offcanvas/index.d.ts +1 -0
  80. package/src/offcanvas/index.js +6 -0
  81. package/src/offcanvas/index.js.map +1 -0
  82. package/src/offcanvas/index.ts +3 -0
  83. package/src/offcanvas/schema.d.ts +1 -0
  84. package/src/offcanvas/schema.js +3 -0
  85. package/src/offcanvas/schema.js.map +1 -0
  86. package/src/offcanvas/schema.json +29 -0
  87. package/src/offcanvas/schema.ts +1 -0
  88. package/src/popover/files/__name@dasherize__/__name@dasherize__.css.template +156 -0
  89. package/src/popover/files/__name@dasherize__/__name@dasherize__.html.template +40 -0
  90. package/src/popover/files/__name@dasherize__/__name@dasherize__.ts.template +65 -0
  91. package/src/popover/index.d.ts +1 -0
  92. package/src/popover/index.js +6 -0
  93. package/src/popover/index.js.map +1 -0
  94. package/src/popover/index.ts +3 -0
  95. package/src/popover/schema.d.ts +1 -0
  96. package/src/popover/schema.js +3 -0
  97. package/src/popover/schema.js.map +1 -0
  98. package/src/popover/schema.json +29 -0
  99. package/src/popover/schema.ts +1 -0
  100. package/src/select/files/__name@dasherize__/__name@dasherize__.css.template +92 -0
  101. package/src/select/files/__name@dasherize__/__name@dasherize__.html.template +33 -0
  102. package/src/select/files/__name@dasherize__/__name@dasherize__.ts.template +51 -0
  103. package/src/select/index.d.ts +1 -0
  104. package/src/select/index.js +6 -0
  105. package/src/select/index.js.map +1 -0
  106. package/src/select/index.ts +3 -0
  107. package/src/select/schema.d.ts +1 -0
  108. package/src/select/schema.js +3 -0
  109. package/src/select/schema.js.map +1 -0
  110. package/src/select/schema.json +29 -0
  111. package/src/select/schema.ts +1 -0
  112. package/src/spinner/files/__name@dasherize__/__name@dasherize__.css.template +85 -0
  113. package/src/spinner/files/__name@dasherize__/__name@dasherize__.html.template +18 -0
  114. package/src/spinner/files/__name@dasherize__/__name@dasherize__.ts.template +33 -0
  115. package/src/spinner/index.d.ts +1 -0
  116. package/src/spinner/index.js +6 -0
  117. package/src/spinner/index.js.map +1 -0
  118. package/src/spinner/index.ts +3 -0
  119. package/src/spinner/schema.d.ts +1 -0
  120. package/src/spinner/schema.js +3 -0
  121. package/src/spinner/schema.js.map +1 -0
  122. package/src/spinner/schema.json +29 -0
  123. package/src/spinner/schema.ts +1 -0
  124. package/src/utils/generate-component.d.ts +1 -1
  125. package/src/utils/generate-component.js +1 -1
  126. package/src/utils/generate-component.ts +1 -1
@@ -0,0 +1,121 @@
1
+ :host {
2
+ --semmet-color-primary: #18181b;
3
+ --semmet-color-on-primary: #ffffff;
4
+ --semmet-color-surface: #ffffff;
5
+ --semmet-color-surface-muted: #f4f4f5;
6
+ --semmet-color-outline: #d4d4d8;
7
+ --semmet-color-on-surface: #27272a;
8
+ --semmet-color-on-surface-variant: #71717a;
9
+ --semmet-radius-md: 10px;
10
+ --semmet-elevation-1: 0 1px 2px rgba(20, 24, 23, 0.1), 0 1px 1px rgba(20, 24, 23, 0.06);
11
+ --semmet-motion: 160ms cubic-bezier(0.2, 0, 0, 1);
12
+
13
+ display: block;
14
+ font-family: Roboto, system-ui, -apple-system, 'Segoe UI', sans-serif;
15
+ font-size: 0.9375rem;
16
+ }
17
+
18
+ .semmet-list-group {
19
+ display: grid;
20
+ margin: 0;
21
+ padding: 0;
22
+ overflow: hidden;
23
+ border: 1px solid var(--semmet-color-outline);
24
+ border-radius: var(--semmet-radius-md);
25
+ background: var(--semmet-color-surface);
26
+ box-shadow: var(--semmet-elevation-1);
27
+ list-style: none;
28
+ }
29
+
30
+ .semmet-list-group--flush {
31
+ border-radius: 0;
32
+ box-shadow: none;
33
+ }
34
+
35
+ .semmet-list-group__item + .semmet-list-group__item {
36
+ border-block-start: 1px solid var(--semmet-color-outline);
37
+ }
38
+
39
+ .semmet-list-group__action {
40
+ display: flex;
41
+ align-items: center;
42
+ justify-content: space-between;
43
+ gap: 1rem;
44
+ width: 100%;
45
+ min-height: 4rem;
46
+ padding: 0.875rem 1rem;
47
+ border: 0;
48
+ color: var(--semmet-color-on-surface);
49
+ background: var(--semmet-color-surface);
50
+ font: inherit;
51
+ text-align: start;
52
+ text-decoration: none;
53
+ cursor: pointer;
54
+ transition:
55
+ background-color var(--semmet-motion),
56
+ color var(--semmet-motion),
57
+ opacity var(--semmet-motion);
58
+ }
59
+
60
+ .semmet-list-group__action:hover:not(:disabled):not(.semmet-list-group__action--disabled) {
61
+ background: var(--semmet-color-surface-muted);
62
+ }
63
+
64
+ .semmet-list-group__action--active {
65
+ color: var(--semmet-color-on-primary);
66
+ background: var(--semmet-color-primary);
67
+ }
68
+
69
+ .semmet-list-group__action:disabled,
70
+ .semmet-list-group__action--disabled {
71
+ cursor: not-allowed;
72
+ opacity: 0.54;
73
+ }
74
+
75
+ .semmet-list-group__action:focus-visible {
76
+ position: relative;
77
+ z-index: 1;
78
+ outline: 2px solid var(--semmet-color-primary);
79
+ outline-offset: -2px;
80
+ }
81
+
82
+ .semmet-list-group__content {
83
+ display: grid;
84
+ min-width: 0;
85
+ gap: 0.25rem;
86
+ }
87
+
88
+ .semmet-list-group__title {
89
+ font-weight: 700;
90
+ line-height: 1.25;
91
+ }
92
+
93
+ .semmet-list-group__description {
94
+ color: var(--semmet-color-on-surface-variant);
95
+ font-size: 0.875rem;
96
+ line-height: 1.4;
97
+ }
98
+
99
+ .semmet-list-group__action--active .semmet-list-group__description {
100
+ color: rgba(255, 255, 255, 0.76);
101
+ }
102
+
103
+ .semmet-list-group__badge {
104
+ display: inline-flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ min-width: 1.5rem;
108
+ min-height: 1.5rem;
109
+ padding: 0.25rem 0.5rem;
110
+ border-radius: 999px;
111
+ color: var(--semmet-color-on-primary);
112
+ background: var(--semmet-color-primary);
113
+ font-size: 0.75rem;
114
+ font-weight: 700;
115
+ line-height: 1;
116
+ }
117
+
118
+ .semmet-list-group__action--active .semmet-list-group__badge {
119
+ color: var(--semmet-color-primary);
120
+ background: var(--semmet-color-on-primary);
121
+ }
@@ -0,0 +1,47 @@
1
+ <!-- Semmet Angular: List Group - semantic list with optional action and active states. -->
2
+ <ul [id]="instanceId" [class]="listClasses()" [attr.aria-label]="label()">
3
+ @for (item of items(); track item.id) {
4
+ <li class="semmet-list-group__item">
5
+ @if (item.href) {
6
+ <a
7
+ class="semmet-list-group__action"
8
+ [class.semmet-list-group__action--active]="isActive(item)"
9
+ [class.semmet-list-group__action--disabled]="item.disabled"
10
+ [href]="item.disabled ? null : item.href"
11
+ [attr.aria-current]="isActive(item) ? 'page' : null"
12
+ [attr.aria-disabled]="item.disabled ? 'true' : null"
13
+ (click)="select(item)"
14
+ >
15
+ <span class="semmet-list-group__content">
16
+ <span class="semmet-list-group__title">{{ item.title }}</span>
17
+ @if (item.description) {
18
+ <span class="semmet-list-group__description">{{ item.description }}</span>
19
+ }
20
+ </span>
21
+ @if (item.badge) {
22
+ <span class="semmet-list-group__badge">{{ item.badge }}</span>
23
+ }
24
+ </a>
25
+ } @else {
26
+ <button
27
+ type="button"
28
+ class="semmet-list-group__action"
29
+ [class.semmet-list-group__action--active]="isActive(item)"
30
+ [disabled]="item.disabled"
31
+ [attr.aria-current]="isActive(item) ? 'true' : null"
32
+ (click)="select(item)"
33
+ >
34
+ <span class="semmet-list-group__content">
35
+ <span class="semmet-list-group__title">{{ item.title }}</span>
36
+ @if (item.description) {
37
+ <span class="semmet-list-group__description">{{ item.description }}</span>
38
+ }
39
+ </span>
40
+ @if (item.badge) {
41
+ <span class="semmet-list-group__badge">{{ item.badge }}</span>
42
+ }
43
+ </button>
44
+ }
45
+ </li>
46
+ }
47
+ </ul>
@@ -0,0 +1,55 @@
1
+ import { Component, booleanAttribute, computed, input, signal } from '@angular/core';
2
+
3
+ interface <%= classify(name) %>Item {
4
+ id: string;
5
+ title: string;
6
+ description?: string;
7
+ badge?: string;
8
+ href?: string;
9
+ disabled?: boolean;
10
+ active?: boolean;
11
+ }
12
+
13
+ const DEFAULT_ITEMS: readonly <%= classify(name) %>Item[] = [
14
+ { id: 'profile', title: 'Profile', description: 'Personal details and public identity', active: true },
15
+ { id: 'security', title: 'Security', description: 'Password, sessions, and two-factor settings', badge: '2' },
16
+ { id: 'billing', title: 'Billing', description: 'Invoices and payment methods' },
17
+ ];
18
+
19
+ let nextId = 0;
20
+
21
+ @Component({
22
+ selector: '<%= selector %>',
23
+ imports: [],
24
+ templateUrl: './<%= dasherize(name) %>.html',
25
+ styleUrl: './<%= dasherize(name) %>.css',
26
+ })
27
+ export class <%= classify(name) %> {
28
+ protected readonly instanceId = `<%= dasherize(name) %>-${nextId++}`;
29
+
30
+ readonly label = input('Settings sections');
31
+ readonly flush = input(false, { transform: booleanAttribute });
32
+ readonly selectable = input(true, { transform: booleanAttribute });
33
+ readonly items = input<readonly <%= classify(name) %>Item[]>(DEFAULT_ITEMS);
34
+
35
+ protected readonly selectedId = signal<string | null>(
36
+ DEFAULT_ITEMS.find((item) => item.active)?.id ?? DEFAULT_ITEMS[0]?.id ?? null,
37
+ );
38
+
39
+ protected readonly listClasses = computed(() => [
40
+ 'semmet-list-group',
41
+ this.flush() ? 'semmet-list-group--flush' : '',
42
+ ].filter(Boolean).join(' '));
43
+
44
+ protected isActive(item: <%= classify(name) %>Item): boolean {
45
+ return this.selectable() ? this.selectedId() === item.id : Boolean(item.active);
46
+ }
47
+
48
+ protected select(item: <%= classify(name) %>Item): void {
49
+ if (!this.selectable() || item.disabled) {
50
+ return;
51
+ }
52
+
53
+ this.selectedId.set(item.id);
54
+ }
55
+ }
@@ -0,0 +1 @@
1
+ export declare const listGroup: (options: import("./schema").Schema) => import("@angular-devkit/schematics").Rule;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listGroup = void 0;
4
+ const generate_component_1 = require("../utils/generate-component");
5
+ exports.listGroup = (0, generate_component_1.createAriaComponentSchematic)();
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;AAAA,oEAA2E;AAE9D,QAAA,SAAS,GAAG,IAAA,iDAA4B,GAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { createAriaComponentSchematic } from '../utils/generate-component';
2
+
3
+ export const listGroup = createAriaComponentSchematic();
@@ -0,0 +1 @@
1
+ export { ComponentSchematicOptions as Schema } from '../utils/schema-types';
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["schema.ts"],"names":[],"mappings":""}
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema",
3
+ "$id": "SemmetAngularListGroup",
4
+ "title": "Semmet Angular List Group Options Schema",
5
+ "type": "object",
6
+ "description": "Generates a standalone Angular List Group component with semantic list structure, active state, optional badges, and action items.",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "name": {
10
+ "type": "string",
11
+ "description": "The name for the new component. Used to derive the class name, selector, and file names.",
12
+ "$default": { "$source": "argv", "index": 0 },
13
+ "x-prompt": "What name would you like to use for the list group component?"
14
+ },
15
+ "path": {
16
+ "type": "string",
17
+ "format": "path",
18
+ "$default": { "$source": "workingDirectory" },
19
+ "description": "The path where the component files should be created, relative to the workspace root. Defaults to the project's source root.",
20
+ "visible": false
21
+ },
22
+ "project": {
23
+ "type": "string",
24
+ "description": "The name of the project to add the component to.",
25
+ "$default": { "$source": "projectName" }
26
+ }
27
+ },
28
+ "required": ["name", "project"]
29
+ }
@@ -0,0 +1 @@
1
+ export { ComponentSchematicOptions as Schema } from '../utils/schema-types';
@@ -0,0 +1,158 @@
1
+ :host {
2
+ --semmet-color-primary: #18181b;
3
+ --semmet-color-surface: #ffffff;
4
+ --semmet-color-surface-muted: #f4f4f5;
5
+ --semmet-color-outline: #d4d4d8;
6
+ --semmet-color-on-surface: #27272a;
7
+ --semmet-color-on-surface-variant: #71717a;
8
+ --semmet-radius-md: 10px;
9
+ --semmet-elevation-1: 0 1px 2px rgba(20, 24, 23, 0.1), 0 1px 1px rgba(20, 24, 23, 0.06);
10
+ --semmet-motion: 160ms cubic-bezier(0.2, 0, 0, 1);
11
+
12
+ display: block;
13
+ font-family: Roboto, system-ui, -apple-system, 'Segoe UI', sans-serif;
14
+ }
15
+
16
+ .semmet-navbar {
17
+ display: grid;
18
+ grid-template-columns: 1fr auto;
19
+ align-items: center;
20
+ gap: 0.75rem;
21
+ padding: 0.75rem;
22
+ border: 1px solid var(--semmet-color-outline);
23
+ border-radius: var(--semmet-radius-md);
24
+ color: var(--semmet-color-on-surface);
25
+ background: var(--semmet-color-surface);
26
+ box-shadow: var(--semmet-elevation-1);
27
+ }
28
+
29
+ .semmet-navbar__brand {
30
+ display: inline-flex;
31
+ align-items: center;
32
+ min-height: 2.5rem;
33
+ color: inherit;
34
+ font-size: 1.0625rem;
35
+ font-weight: 800;
36
+ text-decoration: none;
37
+ }
38
+
39
+ .semmet-navbar__toggle {
40
+ position: relative;
41
+ display: inline-flex;
42
+ align-items: center;
43
+ justify-content: center;
44
+ width: 2.5rem;
45
+ height: 2.5rem;
46
+ border: 1px solid var(--semmet-color-outline);
47
+ border-radius: var(--semmet-radius-md);
48
+ color: inherit;
49
+ background: var(--semmet-color-surface);
50
+ cursor: pointer;
51
+ }
52
+
53
+ .semmet-navbar__toggle span,
54
+ .semmet-navbar__toggle span::before,
55
+ .semmet-navbar__toggle span::after {
56
+ display: block;
57
+ width: 1.125rem;
58
+ height: 0.125rem;
59
+ border-radius: 999px;
60
+ background: currentColor;
61
+ }
62
+
63
+ .semmet-navbar__toggle span {
64
+ position: relative;
65
+ }
66
+
67
+ .semmet-navbar__toggle span::before,
68
+ .semmet-navbar__toggle span::after {
69
+ position: absolute;
70
+ left: 0;
71
+ content: '';
72
+ }
73
+
74
+ .semmet-navbar__toggle span::before {
75
+ top: -0.375rem;
76
+ }
77
+
78
+ .semmet-navbar__toggle span::after {
79
+ top: 0.375rem;
80
+ }
81
+
82
+ .semmet-navbar__menu {
83
+ display: none;
84
+ grid-column: 1 / -1;
85
+ }
86
+
87
+ .semmet-navbar__menu--open {
88
+ display: block;
89
+ }
90
+
91
+ .semmet-navbar__list {
92
+ display: grid;
93
+ gap: 0.25rem;
94
+ margin: 0;
95
+ padding: 0.5rem 0 0;
96
+ list-style: none;
97
+ }
98
+
99
+ .semmet-navbar__link {
100
+ display: flex;
101
+ align-items: center;
102
+ min-height: 2.5rem;
103
+ padding: 0.5rem 0.75rem;
104
+ border-radius: var(--semmet-radius-md);
105
+ color: var(--semmet-color-on-surface-variant);
106
+ font-weight: 650;
107
+ text-decoration: none;
108
+ transition:
109
+ background-color var(--semmet-motion),
110
+ color var(--semmet-motion),
111
+ opacity var(--semmet-motion);
112
+ }
113
+
114
+ .semmet-navbar__link:hover:not(.semmet-navbar__link--disabled) {
115
+ color: var(--semmet-color-on-surface);
116
+ background: var(--semmet-color-surface-muted);
117
+ }
118
+
119
+ .semmet-navbar__link--current {
120
+ color: var(--semmet-color-on-surface);
121
+ background: var(--semmet-color-surface-muted);
122
+ }
123
+
124
+ .semmet-navbar__link--disabled {
125
+ cursor: not-allowed;
126
+ opacity: 0.52;
127
+ }
128
+
129
+ .semmet-navbar__brand:focus-visible,
130
+ .semmet-navbar__toggle:focus-visible,
131
+ .semmet-navbar__link:focus-visible {
132
+ outline: 2px solid var(--semmet-color-primary);
133
+ outline-offset: 2px;
134
+ }
135
+
136
+ @media (min-width: 48rem) {
137
+ .semmet-navbar {
138
+ grid-template-columns: auto 1fr;
139
+ padding-inline: 1rem;
140
+ }
141
+
142
+ .semmet-navbar__toggle {
143
+ display: none;
144
+ }
145
+
146
+ .semmet-navbar__menu {
147
+ display: block;
148
+ grid-column: auto;
149
+ }
150
+
151
+ .semmet-navbar__list {
152
+ display: flex;
153
+ align-items: center;
154
+ justify-content: flex-end;
155
+ gap: 0.25rem;
156
+ padding: 0;
157
+ }
158
+ }
@@ -0,0 +1,36 @@
1
+ <!-- Semmet Angular: Navbar - semantic navigation with responsive disclosure behavior. -->
2
+ <!-- Deliberately avoids role="menu"; regular site navigation should stay as nav/list/link semantics. -->
3
+ <nav class="semmet-navbar" [attr.aria-label]="label()">
4
+ <a class="semmet-navbar__brand" [href]="brandHref()">{{ brand() }}</a>
5
+
6
+ <button
7
+ type="button"
8
+ class="semmet-navbar__toggle"
9
+ [attr.aria-label]="toggleLabel()"
10
+ [attr.aria-controls]="instanceId + '-menu'"
11
+ [attr.aria-expanded]="expanded()"
12
+ (click)="toggle()"
13
+ >
14
+ <span aria-hidden="true"></span>
15
+ </button>
16
+
17
+ <div [id]="instanceId + '-menu'" [class]="menuClasses()">
18
+ <ul class="semmet-navbar__list">
19
+ @for (item of items(); track item.href) {
20
+ <li class="semmet-navbar__item">
21
+ <a
22
+ class="semmet-navbar__link"
23
+ [class.semmet-navbar__link--current]="item.current"
24
+ [class.semmet-navbar__link--disabled]="item.disabled"
25
+ [href]="item.disabled ? null : item.href"
26
+ [attr.aria-current]="item.current ? 'page' : null"
27
+ [attr.aria-disabled]="item.disabled ? 'true' : null"
28
+ (click)="close()"
29
+ >
30
+ {{ item.label }}
31
+ </a>
32
+ </li>
33
+ }
34
+ </ul>
35
+ </div>
36
+ </nav>
@@ -0,0 +1,47 @@
1
+ import { Component, computed, input, signal } from '@angular/core';
2
+
3
+ interface <%= classify(name) %>Item {
4
+ label: string;
5
+ href: string;
6
+ current?: boolean;
7
+ disabled?: boolean;
8
+ }
9
+
10
+ const DEFAULT_ITEMS: readonly <%= classify(name) %>Item[] = [
11
+ { label: 'Home', href: '/', current: true },
12
+ { label: 'Projects', href: '/projects' },
13
+ { label: 'About', href: '/about' },
14
+ { label: 'Contact', href: '/contact' },
15
+ ];
16
+
17
+ let nextId = 0;
18
+
19
+ @Component({
20
+ selector: '<%= selector %>',
21
+ imports: [],
22
+ templateUrl: './<%= dasherize(name) %>.html',
23
+ styleUrl: './<%= dasherize(name) %>.css',
24
+ })
25
+ export class <%= classify(name) %> {
26
+ protected readonly instanceId = `<%= dasherize(name) %>-${nextId++}`;
27
+ protected readonly expanded = signal(false);
28
+
29
+ readonly label = input('Primary navigation');
30
+ readonly brand = input('Semmet');
31
+ readonly brandHref = input('/');
32
+ readonly toggleLabel = input('Toggle navigation');
33
+ readonly items = input<readonly <%= classify(name) %>Item[]>(DEFAULT_ITEMS);
34
+
35
+ protected readonly menuClasses = computed(() => [
36
+ 'semmet-navbar__menu',
37
+ this.expanded() ? 'semmet-navbar__menu--open' : '',
38
+ ].filter(Boolean).join(' '));
39
+
40
+ protected toggle(): void {
41
+ this.expanded.update((value) => !value);
42
+ }
43
+
44
+ protected close(): void {
45
+ this.expanded.set(false);
46
+ }
47
+ }
@@ -0,0 +1 @@
1
+ export declare const navbar: (options: import("./schema").Schema) => import("@angular-devkit/schematics").Rule;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.navbar = void 0;
4
+ const generate_component_1 = require("../utils/generate-component");
5
+ exports.navbar = (0, generate_component_1.createAriaComponentSchematic)();
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;AAAA,oEAA2E;AAE9D,QAAA,MAAM,GAAG,IAAA,iDAA4B,GAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { createAriaComponentSchematic } from '../utils/generate-component';
2
+
3
+ export const navbar = createAriaComponentSchematic();
@@ -0,0 +1 @@
1
+ export { ComponentSchematicOptions as Schema } from '../utils/schema-types';
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["schema.ts"],"names":[],"mappings":""}
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema",
3
+ "$id": "SemmetAngularNavbar",
4
+ "title": "Semmet Angular Navbar Options Schema",
5
+ "type": "object",
6
+ "description": "Generates a standalone Angular Navbar component with semantic navigation, responsive disclosure toggle, and current-page state.",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "name": {
10
+ "type": "string",
11
+ "description": "The name for the new component. Used to derive the class name, selector, and file names.",
12
+ "$default": { "$source": "argv", "index": 0 },
13
+ "x-prompt": "What name would you like to use for the navbar component?"
14
+ },
15
+ "path": {
16
+ "type": "string",
17
+ "format": "path",
18
+ "$default": { "$source": "workingDirectory" },
19
+ "description": "The path where the component files should be created, relative to the workspace root. Defaults to the project's source root.",
20
+ "visible": false
21
+ },
22
+ "project": {
23
+ "type": "string",
24
+ "description": "The name of the project to add the component to.",
25
+ "$default": { "$source": "projectName" }
26
+ }
27
+ },
28
+ "required": ["name", "project"]
29
+ }
@@ -0,0 +1 @@
1
+ export { ComponentSchematicOptions as Schema } from '../utils/schema-types';