semmet-angular 0.10.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 (127) 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/skeleton/files/__name@dasherize__/__name@dasherize__.html.template +1 -0
  113. package/src/spinner/files/__name@dasherize__/__name@dasherize__.css.template +85 -0
  114. package/src/spinner/files/__name@dasherize__/__name@dasherize__.html.template +18 -0
  115. package/src/spinner/files/__name@dasherize__/__name@dasherize__.ts.template +33 -0
  116. package/src/spinner/index.d.ts +1 -0
  117. package/src/spinner/index.js +6 -0
  118. package/src/spinner/index.js.map +1 -0
  119. package/src/spinner/index.ts +3 -0
  120. package/src/spinner/schema.d.ts +1 -0
  121. package/src/spinner/schema.js +3 -0
  122. package/src/spinner/schema.js.map +1 -0
  123. package/src/spinner/schema.json +29 -0
  124. package/src/spinner/schema.ts +1 -0
  125. package/src/utils/generate-component.d.ts +1 -1
  126. package/src/utils/generate-component.js +1 -1
  127. package/src/utils/generate-component.ts +1 -1
@@ -0,0 +1,105 @@
1
+ :host {
2
+ --semmet-color-primary: #18181b;
3
+ --semmet-color-on-surface: #27272a;
4
+ --semmet-color-on-inverse: #ffffff;
5
+ --semmet-color-hover: #f4f4f5;
6
+ --semmet-radius-full: 999px;
7
+ --semmet-motion: 160ms cubic-bezier(0.2, 0, 0, 1);
8
+
9
+ display: inline-block;
10
+ font-family: Roboto, system-ui, -apple-system, 'Segoe UI', sans-serif;
11
+ }
12
+
13
+ .semmet-close-button {
14
+ position: relative;
15
+ display: inline-flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ border: 0;
19
+ border-radius: var(--semmet-radius-full);
20
+ color: var(--semmet-color-on-surface);
21
+ background: transparent;
22
+ cursor: pointer;
23
+ transition:
24
+ background-color var(--semmet-motion),
25
+ color var(--semmet-motion),
26
+ opacity var(--semmet-motion),
27
+ transform var(--semmet-motion);
28
+ }
29
+
30
+ .semmet-close-button--sm {
31
+ width: 2rem;
32
+ height: 2rem;
33
+ }
34
+
35
+ .semmet-close-button--md {
36
+ width: 2.5rem;
37
+ height: 2.5rem;
38
+ }
39
+
40
+ .semmet-close-button--lg {
41
+ width: 3rem;
42
+ height: 3rem;
43
+ }
44
+
45
+ .semmet-close-button--default:hover:not(:disabled),
46
+ .semmet-close-button--subtle {
47
+ background: var(--semmet-color-hover);
48
+ }
49
+
50
+ .semmet-close-button--inverse {
51
+ color: var(--semmet-color-on-inverse);
52
+ }
53
+
54
+ .semmet-close-button--inverse:hover:not(:disabled) {
55
+ background: rgba(255, 255, 255, 0.16);
56
+ }
57
+
58
+ .semmet-close-button:hover:not(:disabled) {
59
+ transform: scale(1.04);
60
+ }
61
+
62
+ .semmet-close-button:active:not(:disabled) {
63
+ transform: scale(0.98);
64
+ }
65
+
66
+ .semmet-close-button:disabled {
67
+ cursor: not-allowed;
68
+ opacity: 0.48;
69
+ }
70
+
71
+ .semmet-close-button:focus-visible {
72
+ outline: 2px solid var(--semmet-color-primary);
73
+ outline-offset: 2px;
74
+ }
75
+
76
+ .semmet-close-button__icon,
77
+ .semmet-close-button__icon::before,
78
+ .semmet-close-button__icon::after {
79
+ display: block;
80
+ width: 1em;
81
+ height: 0.125em;
82
+ }
83
+
84
+ .semmet-close-button__icon {
85
+ position: relative;
86
+ font-size: 1rem;
87
+ }
88
+
89
+ .semmet-close-button__icon::before,
90
+ .semmet-close-button__icon::after {
91
+ position: absolute;
92
+ top: 50%;
93
+ left: 0;
94
+ border-radius: 999px;
95
+ background: currentColor;
96
+ content: '';
97
+ }
98
+
99
+ .semmet-close-button__icon::before {
100
+ transform: rotate(45deg);
101
+ }
102
+
103
+ .semmet-close-button__icon::after {
104
+ transform: rotate(-45deg);
105
+ }
@@ -0,0 +1,11 @@
1
+ <!-- Semmet Angular: Close Button - native button with a required accessible name. -->
2
+ <button
3
+ type="button"
4
+ [id]="instanceId"
5
+ [class]="buttonClasses()"
6
+ [disabled]="disabled()"
7
+ [attr.aria-label]="label()"
8
+ (click)="handleClick($event)"
9
+ >
10
+ <span class="semmet-close-button__icon" aria-hidden="true"></span>
11
+ </button>
@@ -0,0 +1,38 @@
1
+ import { Component, booleanAttribute, computed, input, output } from '@angular/core';
2
+
3
+ type <%= classify(name) %>Variant = 'default' | 'subtle' | 'inverse';
4
+ type <%= classify(name) %>Size = 'sm' | 'md' | 'lg';
5
+
6
+ let nextId = 0;
7
+
8
+ @Component({
9
+ selector: '<%= selector %>',
10
+ imports: [],
11
+ templateUrl: './<%= dasherize(name) %>.html',
12
+ styleUrl: './<%= dasherize(name) %>.css',
13
+ })
14
+ export class <%= classify(name) %> {
15
+ protected readonly instanceId = `<%= dasherize(name) %>-${nextId++}`;
16
+
17
+ readonly label = input('Close');
18
+ readonly variant = input<<%= classify(name) %>Variant>('default');
19
+ readonly size = input<<%= classify(name) %>Size>('md');
20
+ readonly disabled = input(false, { transform: booleanAttribute });
21
+ readonly buttonClick = output<MouseEvent>();
22
+
23
+ protected readonly buttonClasses = computed(() => [
24
+ 'semmet-close-button',
25
+ `semmet-close-button--${this.variant()}`,
26
+ `semmet-close-button--${this.size()}`,
27
+ ].join(' '));
28
+
29
+ protected handleClick(event: MouseEvent): void {
30
+ if (this.disabled()) {
31
+ event.preventDefault();
32
+ event.stopPropagation();
33
+ return;
34
+ }
35
+
36
+ this.buttonClick.emit(event);
37
+ }
38
+ }
@@ -0,0 +1 @@
1
+ export declare const closeButton: (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.closeButton = void 0;
4
+ const generate_component_1 = require("../utils/generate-component");
5
+ exports.closeButton = (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,WAAW,GAAG,IAAA,iDAA4B,GAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { createAriaComponentSchematic } from '../utils/generate-component';
2
+
3
+ export const closeButton = 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": "SemmetAngularCloseButton",
4
+ "title": "Semmet Angular Close Button Options Schema",
5
+ "type": "object",
6
+ "description": "Generates a standalone Angular Close Button component with a native button and accessible label.",
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 close button 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';
@@ -41,6 +41,36 @@
41
41
  "factory": "./button/index#button",
42
42
  "schema": "./button/schema.json"
43
43
  },
44
+ "badge": {
45
+ "description": "Generates a standalone Angular Badge component for accessible status labels, counters, and compact metadata.",
46
+ "factory": "./badge/index#badge",
47
+ "schema": "./badge/schema.json"
48
+ },
49
+ "close-button": {
50
+ "description": "Generates a standalone Angular Close Button component with a native button and accessible label.",
51
+ "factory": "./close-button/index#closeButton",
52
+ "schema": "./close-button/schema.json"
53
+ },
54
+ "offcanvas": {
55
+ "description": "Generates a standalone Angular Offcanvas component using modal dialog semantics, focus trap, Escape close, and focus restoration.",
56
+ "factory": "./offcanvas/index#offcanvas",
57
+ "schema": "./offcanvas/schema.json"
58
+ },
59
+ "popover": {
60
+ "description": "Generates a standalone Angular Popover component with a disclosure trigger, non-modal dialog semantics, Escape close, and outside-click dismissal.",
61
+ "factory": "./popover/index#popover",
62
+ "schema": "./popover/schema.json"
63
+ },
64
+ "spinner": {
65
+ "description": "Generates a standalone Angular Spinner component with status semantics and reduced-motion support.",
66
+ "factory": "./spinner/index#spinner",
67
+ "schema": "./spinner/schema.json"
68
+ },
69
+ "button-group": {
70
+ "description": "Generates a standalone Angular Button Group component with grouped native buttons and optional toggle state.",
71
+ "factory": "./button-group/index#buttonGroup",
72
+ "schema": "./button-group/schema.json"
73
+ },
44
74
  "combobox": {
45
75
  "description": "Generates a standalone Angular Combobox component following the W3C ARIA Authoring Practices Guide.",
46
76
  "factory": "./combobox/index#combobox",
@@ -56,6 +86,11 @@
56
86
  "factory": "./navigation-menu/index#navigationMenu",
57
87
  "schema": "./navigation-menu/schema.json"
58
88
  },
89
+ "navbar": {
90
+ "description": "Generates a standalone Angular Navbar component with semantic navigation, responsive disclosure toggle, and current-page state.",
91
+ "factory": "./navbar/index#navbar",
92
+ "schema": "./navbar/schema.json"
93
+ },
59
94
  "pagination": {
60
95
  "description": "Generates a standalone Angular Pagination component with working page state.",
61
96
  "factory": "./pagination/index#pagination",
@@ -101,6 +136,16 @@
101
136
  "factory": "./input/index#input",
102
137
  "schema": "./input/schema.json"
103
138
  },
139
+ "select": {
140
+ "description": "Generates a standalone Angular Select component using native select semantics, label, hint text, validation message wiring, and signal-based value state.",
141
+ "factory": "./select/index#select",
142
+ "schema": "./select/schema.json"
143
+ },
144
+ "input-group": {
145
+ "description": "Generates a standalone Angular Input Group component with a native input, prefix/suffix text, optional action button, and accessible descriptions.",
146
+ "factory": "./input-group/index#inputGroup",
147
+ "schema": "./input-group/schema.json"
148
+ },
104
149
  "textarea": {
105
150
  "description": "Generates a standalone Angular Textarea component with native textarea semantics, label, hint text, validation message wiring, and signal-based value state.",
106
151
  "factory": "./textarea/index#textarea",
@@ -126,6 +171,11 @@
126
171
  "factory": "./listbox/index#listbox",
127
172
  "schema": "./listbox/schema.json"
128
173
  },
174
+ "list-group": {
175
+ "description": "Generates a standalone Angular List Group component with semantic list structure, active state, optional badges, and action items.",
176
+ "factory": "./list-group/index#listGroup",
177
+ "schema": "./list-group/schema.json"
178
+ },
129
179
  "slider": {
130
180
  "description": "Generates a standalone Angular Slider component following the W3C ARIA Authoring Practices Guide.",
131
181
  "factory": "./slider/index#slider",
@@ -0,0 +1,119 @@
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-color-danger: #b42318;
10
+ --semmet-radius-sm: 8px;
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-input-group__label {
19
+ display: inline-block;
20
+ margin-block-end: 0.5rem;
21
+ color: var(--semmet-color-on-surface);
22
+ font-weight: 650;
23
+ }
24
+
25
+ .semmet-input-group__required {
26
+ color: var(--semmet-color-danger);
27
+ }
28
+
29
+ .semmet-input-group {
30
+ display: flex;
31
+ align-items: stretch;
32
+ width: 100%;
33
+ min-height: 2.75rem;
34
+ border: 1px solid var(--semmet-color-outline);
35
+ border-radius: var(--semmet-radius-sm);
36
+ overflow: hidden;
37
+ color: var(--semmet-color-on-surface);
38
+ background: var(--semmet-color-surface);
39
+ transition:
40
+ border-color var(--semmet-motion),
41
+ box-shadow var(--semmet-motion),
42
+ opacity var(--semmet-motion);
43
+ }
44
+
45
+ .semmet-input-group:focus-within {
46
+ outline: 2px solid var(--semmet-color-primary);
47
+ outline-offset: 2px;
48
+ }
49
+
50
+ .semmet-input-group--invalid {
51
+ border-color: var(--semmet-color-danger);
52
+ }
53
+
54
+ .semmet-input-group--disabled {
55
+ opacity: 0.58;
56
+ }
57
+
58
+ .semmet-input-group__addon,
59
+ .semmet-input-group__button {
60
+ display: inline-flex;
61
+ align-items: center;
62
+ justify-content: center;
63
+ padding: 0 0.875rem;
64
+ white-space: nowrap;
65
+ }
66
+
67
+ .semmet-input-group__addon {
68
+ color: var(--semmet-color-on-surface-variant);
69
+ background: var(--semmet-color-surface-muted);
70
+ border-inline-end: 1px solid var(--semmet-color-outline);
71
+ }
72
+
73
+ .semmet-input-group__control + .semmet-input-group__addon {
74
+ border-inline-start: 1px solid var(--semmet-color-outline);
75
+ border-inline-end: 0;
76
+ }
77
+
78
+ .semmet-input-group__control {
79
+ min-width: 0;
80
+ flex: 1 1 auto;
81
+ border: 0;
82
+ color: inherit;
83
+ background: transparent;
84
+ font: inherit;
85
+ padding: 0.625rem 0.875rem;
86
+ }
87
+
88
+ .semmet-input-group__control:focus {
89
+ outline: 0;
90
+ }
91
+
92
+ .semmet-input-group__button {
93
+ border: 0;
94
+ border-inline-start: 1px solid var(--semmet-color-outline);
95
+ color: var(--semmet-color-on-primary);
96
+ background: var(--semmet-color-primary);
97
+ font: inherit;
98
+ font-weight: 650;
99
+ cursor: pointer;
100
+ }
101
+
102
+ .semmet-input-group__button:disabled {
103
+ cursor: not-allowed;
104
+ }
105
+
106
+ .semmet-input-group__hint,
107
+ .semmet-input-group__error {
108
+ margin: 0.375rem 0 0;
109
+ font-size: 0.875rem;
110
+ line-height: 1.4;
111
+ }
112
+
113
+ .semmet-input-group__hint {
114
+ color: var(--semmet-color-on-surface-variant);
115
+ }
116
+
117
+ .semmet-input-group__error {
118
+ color: var(--semmet-color-danger);
119
+ }
@@ -0,0 +1,51 @@
1
+ <!-- Semmet Angular: Input Group - native input with addon text announced through aria-describedby. -->
2
+ <label class="semmet-input-group__label" [for]="instanceId">
3
+ {{ label() }}
4
+ @if (required()) {
5
+ <span class="semmet-input-group__required" aria-hidden="true">*</span>
6
+ }
7
+ </label>
8
+ <div
9
+ class="semmet-input-group"
10
+ [class.semmet-input-group--invalid]="hasError()"
11
+ [class.semmet-input-group--disabled]="disabled()"
12
+ >
13
+ @if (hasPrefix()) {
14
+ <span class="semmet-input-group__addon" [id]="instanceId + '-prefix'">
15
+ {{ prefix() }}
16
+ </span>
17
+ }
18
+ <input
19
+ [id]="instanceId"
20
+ class="semmet-input-group__control"
21
+ [type]="type()"
22
+ [value]="value()"
23
+ [placeholder]="placeholder()"
24
+ [required]="required()"
25
+ [disabled]="disabled()"
26
+ [attr.aria-describedby]="describedBy()"
27
+ [attr.aria-invalid]="hasError()"
28
+ (input)="handleInput($event)"
29
+ />
30
+ @if (hasSuffix()) {
31
+ <span class="semmet-input-group__addon" [id]="instanceId + '-suffix'">
32
+ {{ suffix() }}
33
+ </span>
34
+ }
35
+ @if (hasAction()) {
36
+ <button
37
+ type="button"
38
+ class="semmet-input-group__button"
39
+ [disabled]="disabled()"
40
+ (click)="handleButtonClick($event)"
41
+ >
42
+ {{ buttonLabel() }}
43
+ </button>
44
+ }
45
+ </div>
46
+ @if (hint()) {
47
+ <p class="semmet-input-group__hint" [id]="instanceId + '-hint'">{{ hint() }}</p>
48
+ }
49
+ @if (hasError()) {
50
+ <p class="semmet-input-group__error" [id]="instanceId + '-error'">{{ errorMessage() }}</p>
51
+ }
@@ -0,0 +1,64 @@
1
+ import { Component, booleanAttribute, computed, input, model, output } from '@angular/core';
2
+
3
+ type <%= classify(name) %>InputType = 'text' | 'email' | 'search' | 'tel' | 'url';
4
+
5
+ let nextId = 0;
6
+
7
+ @Component({
8
+ selector: '<%= selector %>',
9
+ imports: [],
10
+ templateUrl: './<%= dasherize(name) %>.html',
11
+ styleUrl: './<%= dasherize(name) %>.css',
12
+ })
13
+ export class <%= classify(name) %> {
14
+ protected readonly instanceId = `<%= dasherize(name) %>-${nextId++}`;
15
+
16
+ readonly label = input('Website');
17
+ readonly type = input<<%= classify(name) %>InputType>('url');
18
+ readonly placeholder = input('example');
19
+ readonly prefix = input('https://');
20
+ readonly suffix = input('.com');
21
+ readonly buttonLabel = input('Apply');
22
+ readonly hint = input('Use a short, memorable address.');
23
+ readonly errorMessage = input('');
24
+ readonly required = input(false, { transform: booleanAttribute });
25
+ readonly disabled = input(false, { transform: booleanAttribute });
26
+ readonly value = model('');
27
+ readonly buttonClick = output<MouseEvent>();
28
+
29
+ protected readonly hasPrefix = computed(() => this.prefix().trim().length > 0);
30
+ protected readonly hasSuffix = computed(() => this.suffix().trim().length > 0);
31
+ protected readonly hasAction = computed(() => this.buttonLabel().trim().length > 0);
32
+ protected readonly hasError = computed(() => this.errorMessage().trim().length > 0);
33
+
34
+ protected readonly describedBy = computed(() => {
35
+ const ids: string[] = [];
36
+ if (this.hasPrefix()) {
37
+ ids.push(`${this.instanceId}-prefix`);
38
+ }
39
+ if (this.hasSuffix()) {
40
+ ids.push(`${this.instanceId}-suffix`);
41
+ }
42
+ if (this.hint().trim().length > 0) {
43
+ ids.push(`${this.instanceId}-hint`);
44
+ }
45
+ if (this.hasError()) {
46
+ ids.push(`${this.instanceId}-error`);
47
+ }
48
+ return ids.length > 0 ? ids.join(' ') : null;
49
+ });
50
+
51
+ protected handleInput(event: Event): void {
52
+ this.value.set((event.target as HTMLInputElement).value);
53
+ }
54
+
55
+ protected handleButtonClick(event: MouseEvent): void {
56
+ if (this.disabled()) {
57
+ event.preventDefault();
58
+ event.stopPropagation();
59
+ return;
60
+ }
61
+
62
+ this.buttonClick.emit(event);
63
+ }
64
+ }
@@ -0,0 +1 @@
1
+ export declare const inputGroup: (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.inputGroup = void 0;
4
+ const generate_component_1 = require("../utils/generate-component");
5
+ exports.inputGroup = (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,UAAU,GAAG,IAAA,iDAA4B,GAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { createAriaComponentSchematic } from '../utils/generate-component';
2
+
3
+ export const inputGroup = 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": "SemmetAngularInputGroup",
4
+ "title": "Semmet Angular Input Group Options Schema",
5
+ "type": "object",
6
+ "description": "Generates a standalone Angular Input Group component with a native input, prefix/suffix text, optional action button, and accessible descriptions.",
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 input 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';