semmet-angular 0.42.0 → 0.43.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 (202) hide show
  1. package/README.en.md +2 -315
  2. package/README.md +169 -264
  3. package/package.json +2 -2
  4. package/src/accordion/files/__name@dasherize__/__name@dasherize__.css.template +4 -41
  5. package/src/accordion/schema.json +19 -4
  6. package/src/alert/files/__name@dasherize__/__name@dasherize__.css.template +1 -25
  7. package/src/alert/files/__name@dasherize__/__name@dasherize__.html.template +3 -3
  8. package/src/alert/files/__name@dasherize__/__name@dasherize__.ts.template +4 -1
  9. package/src/alert/schema.json +19 -4
  10. package/src/alert-dialog/files/__name@dasherize__/__name@dasherize__.css.template +3 -56
  11. package/src/alert-dialog/files/__name@dasherize__/__name@dasherize__.html.template +6 -6
  12. package/src/alert-dialog/files/__name@dasherize__/__name@dasherize__.ts.template +7 -1
  13. package/src/alert-dialog/schema.json +19 -4
  14. package/src/avatar/files/__name@dasherize__/__name@dasherize__.css.template +17 -0
  15. package/src/avatar/files/__name@dasherize__/__name@dasherize__.html.template +28 -0
  16. package/src/avatar/files/__name@dasherize__/__name@dasherize__.spec.ts.template +27 -0
  17. package/src/avatar/files/__name@dasherize__/__name@dasherize__.ts.template +35 -0
  18. package/src/avatar/index.d.ts +1 -0
  19. package/src/avatar/index.js +6 -0
  20. package/src/avatar/index.js.map +1 -0
  21. package/src/avatar/index.ts +3 -0
  22. package/src/avatar/schema.d.ts +1 -0
  23. package/src/avatar/schema.js +3 -0
  24. package/src/avatar/schema.js.map +1 -0
  25. package/src/avatar/schema.json +44 -0
  26. package/src/avatar/schema.ts +1 -0
  27. package/src/badge/files/__name@dasherize__/__name@dasherize__.css.template +0 -77
  28. package/src/badge/files/__name@dasherize__/__name@dasherize__.ts.template +2 -5
  29. package/src/badge/schema.json +19 -4
  30. package/src/block/files/__name@dasherize__/__name@dasherize__.css.template +10 -81
  31. package/src/block/files/__name@dasherize__/__name@dasherize__.html.template +33 -25
  32. package/src/block/files/__name@dasherize__/__name@dasherize__.ts.template +41 -10
  33. package/src/block/schema.json +19 -4
  34. package/src/breadcrumb/files/__name@dasherize__/__name@dasherize__.css.template +2 -23
  35. package/src/breadcrumb/files/__name@dasherize__/__name@dasherize__.html.template +4 -4
  36. package/src/breadcrumb/files/__name@dasherize__/__name@dasherize__.ts.template +9 -5
  37. package/src/breadcrumb/schema.json +19 -4
  38. package/src/button/files/__name@dasherize__/__name@dasherize__.css.template +2 -79
  39. package/src/button/files/__name@dasherize__/__name@dasherize__.ts.template +2 -5
  40. package/src/button/schema.json +19 -4
  41. package/src/button-group/files/__name@dasherize__/__name@dasherize__.css.template +2 -70
  42. package/src/button-group/files/__name@dasherize__/__name@dasherize__.ts.template +1 -2
  43. package/src/button-group/schema.json +19 -4
  44. package/src/card/files/__name@dasherize__/__name@dasherize__.css.template +0 -67
  45. package/src/card/files/__name@dasherize__/__name@dasherize__.html.template +10 -55
  46. package/src/card/files/__name@dasherize__/__name@dasherize__.ts.template +2 -45
  47. package/src/card/schema.json +19 -4
  48. package/src/carousel/files/__name@dasherize__/__name@dasherize__.css.template +2 -32
  49. package/src/carousel/files/__name@dasherize__/__name@dasherize__.html.template +4 -4
  50. package/src/carousel/files/__name@dasherize__/__name@dasherize__.ts.template +4 -0
  51. package/src/carousel/schema.json +19 -4
  52. package/src/checkbox/files/__name@dasherize__/__name@dasherize__.css.template +5 -21
  53. package/src/checkbox/schema.json +19 -4
  54. package/src/close-button/files/__name@dasherize__/__name@dasherize__.css.template +9 -47
  55. package/src/close-button/files/__name@dasherize__/__name@dasherize__.ts.template +2 -5
  56. package/src/close-button/schema.json +19 -4
  57. package/src/collection.json +15 -0
  58. package/src/combobox/files/__name@dasherize__/__name@dasherize__.css.template +4 -49
  59. package/src/combobox/files/__name@dasherize__/__name@dasherize__.html.template +2 -2
  60. package/src/combobox/files/__name@dasherize__/__name@dasherize__.ts.template +11 -8
  61. package/src/combobox/schema.json +19 -4
  62. package/src/dialog/files/__name@dasherize__/__name@dasherize__.css.template +3 -45
  63. package/src/dialog/files/__name@dasherize__/__name@dasherize__.html.template +5 -5
  64. package/src/dialog/files/__name@dasherize__/__name@dasherize__.ts.template +6 -1
  65. package/src/dialog/schema.json +19 -4
  66. package/src/disclosure/files/__name@dasherize__/__name@dasherize__.css.template +4 -36
  67. package/src/disclosure/schema.json +19 -4
  68. package/src/form/files/__name@dasherize__/__name@dasherize__.css.template +1 -77
  69. package/src/form/files/__name@dasherize__/__name@dasherize__.html.template +7 -7
  70. package/src/form/files/__name@dasherize__/__name@dasherize__.ts.template +20 -7
  71. package/src/form/schema.json +19 -4
  72. package/src/input/files/__name@dasherize__/__name@dasherize__.css.template +2 -58
  73. package/src/input/schema.json +19 -4
  74. package/src/input-group/files/__name@dasherize__/__name@dasherize__.css.template +2 -68
  75. package/src/input-group/files/__name@dasherize__/__name@dasherize__.html.template +13 -5
  76. package/src/input-group/files/__name@dasherize__/__name@dasherize__.ts.template +21 -6
  77. package/src/input-group/schema.json +19 -4
  78. package/src/landmarks/files/__name@dasherize__/__name@dasherize__.css.template +0 -24
  79. package/src/landmarks/files/__name@dasherize__/__name@dasherize__.html.template +2 -2
  80. package/src/landmarks/files/__name@dasherize__/__name@dasherize__.ts.template +5 -2
  81. package/src/landmarks/schema.json +19 -4
  82. package/src/list-group/files/__name@dasherize__/__name@dasherize__.css.template +0 -123
  83. package/src/list-group/files/__name@dasherize__/__name@dasherize__.e2e-spec.ts.template +8 -9
  84. package/src/list-group/files/__name@dasherize__/__name@dasherize__.html.template +13 -47
  85. package/src/list-group/files/__name@dasherize__/__name@dasherize__.spec.ts.template +46 -24
  86. package/src/list-group/files/__name@dasherize__/__name@dasherize__.ts.template +97 -39
  87. package/src/list-group/schema.json +19 -4
  88. package/src/listbox/files/__name@dasherize__/__name@dasherize__.css.template +3 -37
  89. package/src/listbox/files/__name@dasherize__/__name@dasherize__.html.template +2 -2
  90. package/src/listbox/files/__name@dasherize__/__name@dasherize__.ts.template +3 -1
  91. package/src/listbox/schema.json +19 -4
  92. package/src/menu-button/files/__name@dasherize__/__name@dasherize__.css.template +7 -47
  93. package/src/menu-button/files/__name@dasherize__/__name@dasherize__.html.template +3 -3
  94. package/src/menu-button/files/__name@dasherize__/__name@dasherize__.ts.template +10 -7
  95. package/src/menu-button/schema.json +19 -4
  96. package/src/meter/files/__name@dasherize__/__name@dasherize__.css.template +0 -12
  97. package/src/meter/files/__name@dasherize__/__name@dasherize__.html.template +2 -2
  98. package/src/meter/files/__name@dasherize__/__name@dasherize__.ts.template +3 -1
  99. package/src/meter/schema.json +19 -4
  100. package/src/navbar/files/__name@dasherize__/__name@dasherize__.css.template +6 -62
  101. package/src/navbar/schema.json +19 -4
  102. package/src/navigation-menu/files/__name@dasherize__/__name@dasherize__.css.template +6 -37
  103. package/src/navigation-menu/files/__name@dasherize__/__name@dasherize__.html.template +7 -6
  104. package/src/navigation-menu/files/__name@dasherize__/__name@dasherize__.ts.template +15 -1
  105. package/src/navigation-menu/schema.json +19 -4
  106. package/src/offcanvas/files/__name@dasherize__/__name@dasherize__.css.template +3 -70
  107. package/src/offcanvas/schema.json +19 -4
  108. package/src/pagination/files/__name@dasherize__/__name@dasherize__.css.template +2 -33
  109. package/src/pagination/files/__name@dasherize__/__name@dasherize__.html.template +6 -6
  110. package/src/pagination/files/__name@dasherize__/__name@dasherize__.ts.template +7 -1
  111. package/src/pagination/schema.json +19 -4
  112. package/src/popover/files/__name@dasherize__/__name@dasherize__.css.template +8 -69
  113. package/src/popover/schema.json +19 -4
  114. package/src/progress-bar/files/__name@dasherize__/__name@dasherize__.css.template +1 -31
  115. package/src/progress-bar/files/__name@dasherize__/__name@dasherize__.html.template +3 -3
  116. package/src/progress-bar/files/__name@dasherize__/__name@dasherize__.ts.template +5 -1
  117. package/src/progress-bar/schema.json +19 -4
  118. package/src/radio-group/files/__name@dasherize__/__name@dasherize__.css.template +4 -31
  119. package/src/radio-group/files/__name@dasherize__/__name@dasherize__.html.template +2 -2
  120. package/src/radio-group/files/__name@dasherize__/__name@dasherize__.ts.template +2 -1
  121. package/src/radio-group/schema.json +19 -4
  122. package/src/rating/files/__name@dasherize__/__name@dasherize__.css.template +17 -0
  123. package/src/rating/files/__name@dasherize__/__name@dasherize__.html.template +34 -0
  124. package/src/rating/files/__name@dasherize__/__name@dasherize__.spec.ts.template +38 -0
  125. package/src/rating/files/__name@dasherize__/__name@dasherize__.ts.template +75 -0
  126. package/src/rating/index.d.ts +1 -0
  127. package/src/rating/index.js +6 -0
  128. package/src/rating/index.js.map +1 -0
  129. package/src/rating/index.ts +3 -0
  130. package/src/rating/schema.d.ts +1 -0
  131. package/src/rating/schema.js +3 -0
  132. package/src/rating/schema.js.map +1 -0
  133. package/src/rating/schema.json +44 -0
  134. package/src/rating/schema.ts +1 -0
  135. package/src/select/files/__name@dasherize__/__name@dasherize__.css.template +4 -55
  136. package/src/select/schema.json +19 -4
  137. package/src/skeleton/files/__name@dasherize__/__name@dasherize__.css.template +2 -29
  138. package/src/skeleton/files/__name@dasherize__/__name@dasherize__.ts.template +1 -3
  139. package/src/skeleton/schema.json +19 -4
  140. package/src/skip-link/files/__name@dasherize__/__name@dasherize__.css.template +2 -14
  141. package/src/skip-link/files/__name@dasherize__/__name@dasherize__.html.template +2 -2
  142. package/src/skip-link/files/__name@dasherize__/__name@dasherize__.ts.template +5 -2
  143. package/src/skip-link/schema.json +19 -4
  144. package/src/slider/files/__name@dasherize__/__name@dasherize__.css.template +6 -20
  145. package/src/slider/files/__name@dasherize__/__name@dasherize__.html.template +2 -2
  146. package/src/slider/files/__name@dasherize__/__name@dasherize__.ts.template +3 -1
  147. package/src/slider/schema.json +19 -4
  148. package/src/spinbutton/files/__name@dasherize__/__name@dasherize__.css.template +6 -35
  149. package/src/spinbutton/files/__name@dasherize__/__name@dasherize__.html.template +4 -4
  150. package/src/spinbutton/files/__name@dasherize__/__name@dasherize__.ts.template +5 -1
  151. package/src/spinbutton/schema.json +19 -4
  152. package/src/spinner/files/__name@dasherize__/__name@dasherize__.css.template +0 -43
  153. package/src/spinner/files/__name@dasherize__/__name@dasherize__.ts.template +2 -5
  154. package/src/spinner/schema.json +19 -4
  155. package/src/steps/files/__name@dasherize__/__name@dasherize__.css.template +36 -0
  156. package/src/steps/files/__name@dasherize__/__name@dasherize__.html.template +44 -0
  157. package/src/steps/files/__name@dasherize__/__name@dasherize__.spec.ts.template +35 -0
  158. package/src/steps/files/__name@dasherize__/__name@dasherize__.ts.template +107 -0
  159. package/src/steps/index.d.ts +1 -0
  160. package/src/steps/index.js +6 -0
  161. package/src/steps/index.js.map +1 -0
  162. package/src/steps/index.ts +3 -0
  163. package/src/steps/schema.d.ts +1 -0
  164. package/src/steps/schema.js +3 -0
  165. package/src/steps/schema.js.map +1 -0
  166. package/src/steps/schema.json +44 -0
  167. package/src/steps/schema.ts +1 -0
  168. package/src/switch/files/__name@dasherize__/__name@dasherize__.css.template +7 -22
  169. package/src/switch/schema.json +19 -4
  170. package/src/table/files/__name@dasherize__/__name@dasherize__.css.template +0 -25
  171. package/src/table/files/__name@dasherize__/__name@dasherize__.html.template +3 -3
  172. package/src/table/files/__name@dasherize__/__name@dasherize__.ts.template +3 -0
  173. package/src/table/schema.json +19 -4
  174. package/src/tabs/files/__name@dasherize__/__name@dasherize__.css.template +5 -44
  175. package/src/tabs/files/__name@dasherize__/__name@dasherize__.html.template +2 -2
  176. package/src/tabs/files/__name@dasherize__/__name@dasherize__.ts.template +2 -0
  177. package/src/tabs/schema.json +19 -4
  178. package/src/textarea/files/__name@dasherize__/__name@dasherize__.css.template +2 -59
  179. package/src/textarea/schema.json +19 -4
  180. package/src/toast/files/__name@dasherize__/__name@dasherize__.css.template +1 -35
  181. package/src/toast/files/__name@dasherize__/__name@dasherize__.html.template +4 -4
  182. package/src/toast/files/__name@dasherize__/__name@dasherize__.ts.template +5 -1
  183. package/src/toast/schema.json +19 -4
  184. package/src/toolbar/files/__name@dasherize__/__name@dasherize__.css.template +2 -36
  185. package/src/toolbar/files/__name@dasherize__/__name@dasherize__.html.template +3 -3
  186. package/src/toolbar/files/__name@dasherize__/__name@dasherize__.ts.template +11 -8
  187. package/src/toolbar/schema.json +19 -4
  188. package/src/tooltip/files/__name@dasherize__/__name@dasherize__.css.template +1 -32
  189. package/src/tooltip/schema.json +19 -4
  190. package/src/tree-view/files/__name@dasherize__/__name@dasherize__.css.template +5 -27
  191. package/src/tree-view/files/__name@dasherize__/__name@dasherize__.html.template +3 -3
  192. package/src/tree-view/files/__name@dasherize__/__name@dasherize__.ts.template +16 -13
  193. package/src/tree-view/schema.json +19 -4
  194. package/src/utils/generate-component.js +3 -1
  195. package/src/utils/generate-component.js.map +1 -1
  196. package/src/utils/generate-component.ts +3 -1
  197. package/src/utils/generate-composition.js +0 -1
  198. package/src/utils/generate-composition.js.map +1 -1
  199. package/src/utils/generate-composition.ts +0 -1
  200. package/src/utils/schema-types.d.ts +1 -0
  201. package/src/utils/schema-types.ts +1 -0
  202. package/README.pt-BR.md +0 -330
@@ -1,122 +1,46 @@
1
1
  :host {
2
- --semmet-color-primary: #18181b;
3
- --semmet-color-on-primary: #ffffff;
4
- --semmet-color-danger: #b42318;
5
- --semmet-color-surface: #ffffff;
6
- --semmet-color-outline: #d4d4d8;
7
- --semmet-color-on-surface: #18181b;
8
- --semmet-color-on-surface-variant: #52525b;
9
- --semmet-radius-sm: 8px;
10
- --semmet-radius-md: 12px;
11
- --semmet-space-2xs: 8px;
12
- --semmet-space-xs: 16px;
13
- --semmet-space-sm: 24px;
14
- --semmet-target-md: 40px;
15
- --semmet-motion: 160ms cubic-bezier(0.2, 0, 0, 1);
16
-
17
2
  display: block;
18
- max-width: 34rem;
19
- font-family: Roboto, system-ui, -apple-system, 'Segoe UI', sans-serif;
20
- font-size: 0.9375rem;
21
3
  }
22
4
 
23
5
  form {
24
6
  display: grid;
25
- gap: var(--semmet-space-xs);
26
- padding: var(--semmet-space-sm);
27
- color: var(--semmet-color-on-surface);
28
- background: var(--semmet-color-surface);
29
- border: 1px solid var(--semmet-color-outline);
30
- border-radius: var(--semmet-radius-md);
31
7
  }
32
8
 
33
9
  h2 {
34
10
  margin: 0;
35
- font-size: 1.125rem;
36
- font-weight: 600;
37
11
  }
38
12
 
39
13
  .semmet-field {
40
14
  display: grid;
41
- gap: var(--semmet-space-2xs);
42
- }
43
-
44
- label {
45
- font-weight: 500;
46
- }
47
-
48
- label span {
49
- color: var(--semmet-color-danger);
50
15
  }
51
16
 
52
17
  input,
53
18
  textarea {
54
19
  width: 100%;
55
20
  box-sizing: border-box;
56
- border: 1px solid var(--semmet-color-outline);
57
- border-radius: var(--semmet-radius-sm);
58
- padding: var(--semmet-space-2xs) var(--semmet-space-xs);
59
21
  font: inherit;
60
- color: var(--semmet-color-on-surface);
61
- background: var(--semmet-color-surface);
62
- transition: border-color var(--semmet-motion);
63
- }
64
-
65
- input {
66
- min-height: var(--semmet-target-md);
67
22
  }
68
23
 
69
24
  textarea {
70
25
  min-height: 7rem;
71
26
  resize: vertical;
72
- line-height: 1.5;
73
- }
74
-
75
- input[aria-invalid='true'],
76
- textarea[aria-invalid='true'] {
77
- border-color: var(--semmet-color-danger);
78
27
  }
79
28
 
80
29
  input:focus-visible,
81
30
  textarea:focus-visible,
82
31
  button:focus-visible {
83
- outline: 2px solid var(--semmet-color-primary);
32
+ outline: 2px solid currentColor;
84
33
  outline-offset: 2px;
85
34
  }
86
35
 
87
36
  button {
88
37
  justify-self: start;
89
- min-height: var(--semmet-target-md);
90
- border: 0;
91
- border-radius: var(--semmet-radius-sm);
92
- padding: var(--semmet-space-2xs) var(--semmet-space-xs);
93
38
  font: inherit;
94
- font-weight: 600;
95
- color: var(--semmet-color-on-primary);
96
- background: var(--semmet-color-primary);
97
39
  cursor: pointer;
98
- transition:
99
- background-color var(--semmet-motion),
100
- transform var(--semmet-motion);
101
- }
102
-
103
- button:hover {
104
- transform: translateY(-1px);
105
40
  }
106
41
 
107
42
  .semmet-hint,
108
43
  .semmet-error,
109
44
  .semmet-status {
110
45
  margin: 0;
111
- font-size: 0.8125rem;
112
- line-height: 1.4;
113
- }
114
-
115
- .semmet-hint,
116
- .semmet-status {
117
- color: var(--semmet-color-on-surface-variant);
118
- }
119
-
120
- .semmet-error {
121
- color: var(--semmet-color-danger);
122
46
  }
@@ -3,14 +3,14 @@
3
3
  Usage from a parent component template:
4
4
  Import <%= classify(name) %> in that parent component.
5
5
 
6
- <<%= selector %> />
6
+ <<%= selector %> title="Contact form" submitLabel="Submit" />
7
7
  -->
8
8
  <form [attr.aria-labelledby]="instanceId + '-title'" (submit)="submit($event)" novalidate>
9
- <h2 [id]="instanceId + '-title'">Contact form</h2>
9
+ <h2 [id]="instanceId + '-title'">{{ title() }}</h2>
10
10
 
11
11
  <div class="semmet-field">
12
12
  <label [for]="instanceId + '-name'">
13
- Name
13
+ {{ nameLabel() }}
14
14
  <span aria-hidden="true">*</span>
15
15
  </label>
16
16
  <input
@@ -30,7 +30,7 @@ Import <%= classify(name) %> in that parent component.
30
30
 
31
31
  <div class="semmet-field">
32
32
  <label [for]="instanceId + '-email'">
33
- Email
33
+ {{ emailLabel() }}
34
34
  <span aria-hidden="true">*</span>
35
35
  </label>
36
36
  <input
@@ -50,7 +50,7 @@ Import <%= classify(name) %> in that parent component.
50
50
 
51
51
  <div class="semmet-field">
52
52
  <label [for]="instanceId + '-message'">
53
- Message
53
+ {{ messageLabel() }}
54
54
  <span aria-hidden="true">*</span>
55
55
  </label>
56
56
  <textarea
@@ -62,13 +62,13 @@ Import <%= classify(name) %> in that parent component.
62
62
  [attr.aria-describedby]="messageError() ? instanceId + '-message-error' : instanceId + '-message-hint'"
63
63
  (input)="updateField('message', $event)"
64
64
  ></textarea>
65
- <p class="semmet-hint" [id]="instanceId + '-message-hint'">Minimum 10 characters.</p>
65
+ <p class="semmet-hint" [id]="instanceId + '-message-hint'">{{ messageHint() }}</p>
66
66
  @if (messageError()) {
67
67
  <p class="semmet-error" [id]="instanceId + '-message-error'" role="alert">{{ messageError() }}</p>
68
68
  }
69
69
  </div>
70
70
 
71
- <button type="submit">Submit</button>
71
+ <button type="submit">{{ submitLabel() }}</button>
72
72
 
73
73
  @if (statusMessage()) {
74
74
  <p class="semmet-status" role="status">{{ statusMessage() }}</p>
@@ -1,4 +1,4 @@
1
- import { Component, computed, signal } from '@angular/core';
1
+ import { Component, computed, input, signal } from '@angular/core';
2
2
 
3
3
  interface <%= classify(name) %>State {
4
4
  name: string;
@@ -26,12 +26,25 @@ export class <%= classify(name) %> {
26
26
  message: '',
27
27
  });
28
28
 
29
+ readonly title = input('Contact form');
30
+ readonly nameLabel = input('Name');
31
+ readonly emailLabel = input('Email');
32
+ readonly messageLabel = input('Message');
33
+ readonly messageHint = input('Minimum 10 characters.');
34
+ readonly submitLabel = input('Submit');
35
+ readonly nameRequiredMessage = input('Enter your name.');
36
+ readonly emailRequiredMessage = input('Enter your email address.');
37
+ readonly emailInvalidMessage = input('Enter a valid email address.');
38
+ readonly messageRequiredMessage = input('Enter a message with at least 10 characters.');
39
+ readonly invalidStatusMessage = input('Check the highlighted fields and try again.');
40
+ readonly validStatusMessage = input('Form is valid and ready to submit.');
41
+
29
42
  protected readonly nameError = computed(() => {
30
43
  if (!this.submitted() || this.form().name.trim()) {
31
44
  return '';
32
45
  }
33
46
 
34
- return 'Enter your name.';
47
+ return this.nameRequiredMessage();
35
48
  });
36
49
 
37
50
  protected readonly emailError = computed(() => {
@@ -41,10 +54,10 @@ export class <%= classify(name) %> {
41
54
 
42
55
  const email = this.form().email.trim();
43
56
  if (!email) {
44
- return 'Enter your email address.';
57
+ return this.emailRequiredMessage();
45
58
  }
46
59
 
47
- return this.emailPattern.test(email) ? '' : 'Enter a valid email address.';
60
+ return this.emailPattern.test(email) ? '' : this.emailInvalidMessage();
48
61
  });
49
62
 
50
63
  protected readonly messageError = computed(() => {
@@ -52,7 +65,7 @@ export class <%= classify(name) %> {
52
65
  return '';
53
66
  }
54
67
 
55
- return 'Enter a message with at least 10 characters.';
68
+ return this.messageRequiredMessage();
56
69
  });
57
70
 
58
71
  protected readonly isInvalid = computed(() => Boolean(this.nameError() || this.emailError() || this.messageError()));
@@ -67,10 +80,10 @@ export class <%= classify(name) %> {
67
80
  this.submitted.set(true);
68
81
 
69
82
  if (this.isInvalid()) {
70
- this.statusMessage.set('Check the highlighted fields and try again.');
83
+ this.statusMessage.set(this.invalidStatusMessage());
71
84
  return;
72
85
  }
73
86
 
74
- this.statusMessage.set('Form is valid and ready to submit.');
87
+ this.statusMessage.set(this.validStatusMessage());
75
88
  }
76
89
  }
@@ -9,21 +9,36 @@
9
9
  "name": {
10
10
  "type": "string",
11
11
  "description": "The name for the new component. Used to derive the class name, selector, and file names.",
12
- "$default": { "$source": "argv", "index": 0 },
12
+ "$default": {
13
+ "$source": "argv",
14
+ "index": 0
15
+ },
13
16
  "x-prompt": "What name would you like to use for the form component?"
14
17
  },
18
+ "e2e": {
19
+ "type": "boolean",
20
+ "default": false,
21
+ "description": "Generate a Playwright e2e spec for this component. Requires Playwright to be installed/configured in the consuming app."
22
+ },
15
23
  "path": {
16
24
  "type": "string",
17
25
  "format": "path",
18
- "$default": { "$source": "workingDirectory" },
26
+ "$default": {
27
+ "$source": "workingDirectory"
28
+ },
19
29
  "description": "The path where the component files should be created, relative to the workspace root. Defaults to the project's source root.",
20
30
  "visible": false
21
31
  },
22
32
  "project": {
23
33
  "type": "string",
24
34
  "description": "The name of the project to add the component to.",
25
- "$default": { "$source": "projectName" }
35
+ "$default": {
36
+ "$source": "projectName"
37
+ }
26
38
  }
27
39
  },
28
- "required": ["name", "project"]
40
+ "required": [
41
+ "name",
42
+ "project"
43
+ ]
29
44
  }
@@ -1,83 +1,27 @@
1
1
  :host {
2
- --semmet-color-primary: #18181b;
3
- --semmet-color-danger: #b42318;
4
- --semmet-color-surface: #ffffff;
5
- --semmet-color-outline: #d4d4d8;
6
- --semmet-color-on-surface: #18181b;
7
- --semmet-color-on-surface-variant: #52525b;
8
- --semmet-color-disabled: #f4f4f5;
9
- --semmet-radius-sm: 8px;
10
- --semmet-space-2xs: 8px;
11
- --semmet-space-xs: 16px;
12
- --semmet-target-md: 40px;
13
- --semmet-motion: 160ms cubic-bezier(0.2, 0, 0, 1);
14
-
15
2
  display: block;
16
- font-family: Roboto, system-ui, -apple-system, 'Segoe UI', sans-serif;
17
- font-size: 0.9375rem;
18
3
  }
19
4
 
20
5
  .semmet-field {
21
6
  display: grid;
22
- gap: var(--semmet-space-2xs);
23
- }
24
-
25
- label {
26
- font-weight: 500;
27
- color: var(--semmet-color-on-surface);
28
- }
29
-
30
- label span {
31
- color: var(--semmet-color-danger);
32
7
  }
33
8
 
34
9
  input {
35
10
  width: 100%;
36
- min-height: var(--semmet-target-md);
37
11
  box-sizing: border-box;
38
- border: 1px solid var(--semmet-color-outline);
39
- border-radius: var(--semmet-radius-sm);
40
- padding: var(--semmet-space-2xs) var(--semmet-space-xs);
41
12
  font: inherit;
42
- color: var(--semmet-color-on-surface);
43
- background: var(--semmet-color-surface);
44
- transition:
45
- border-color var(--semmet-motion),
46
- box-shadow var(--semmet-motion),
47
- background-color var(--semmet-motion);
48
- }
49
-
50
- input::placeholder {
51
- color: var(--semmet-color-on-surface-variant);
52
- }
53
-
54
- input[aria-invalid='true'] {
55
- border-color: var(--semmet-color-danger);
56
13
  }
57
14
 
58
15
  input:disabled {
59
- color: var(--semmet-color-on-surface-variant);
60
- background: var(--semmet-color-disabled);
61
16
  cursor: not-allowed;
62
17
  }
63
18
 
64
19
  input:focus-visible {
65
- outline: 2px solid var(--semmet-color-primary);
66
- outline-offset: 1px;
67
- border-color: var(--semmet-color-primary);
20
+ outline: 2px solid currentColor;
21
+ outline-offset: 2px;
68
22
  }
69
23
 
70
24
  .semmet-field__hint,
71
25
  .semmet-field__error {
72
26
  margin: 0;
73
- font-size: 0.8125rem;
74
- line-height: 1.4;
75
- }
76
-
77
- .semmet-field__hint {
78
- color: var(--semmet-color-on-surface-variant);
79
- }
80
-
81
- .semmet-field__error {
82
- color: var(--semmet-color-danger);
83
27
  }
@@ -9,21 +9,36 @@
9
9
  "name": {
10
10
  "type": "string",
11
11
  "description": "The name for the new component. Used to derive the class name, selector, and file names.",
12
- "$default": { "$source": "argv", "index": 0 },
12
+ "$default": {
13
+ "$source": "argv",
14
+ "index": 0
15
+ },
13
16
  "x-prompt": "What name would you like to use for the input component?"
14
17
  },
18
+ "e2e": {
19
+ "type": "boolean",
20
+ "default": false,
21
+ "description": "Generate a Playwright e2e spec for this component. Requires Playwright to be installed/configured in the consuming app."
22
+ },
15
23
  "path": {
16
24
  "type": "string",
17
25
  "format": "path",
18
- "$default": { "$source": "workingDirectory" },
26
+ "$default": {
27
+ "$source": "workingDirectory"
28
+ },
19
29
  "description": "The path where the component files should be created, relative to the workspace root. Defaults to the project's source root.",
20
30
  "visible": false
21
31
  },
22
32
  "project": {
23
33
  "type": "string",
24
34
  "description": "The name of the project to add the component to.",
25
- "$default": { "$source": "projectName" }
35
+ "$default": {
36
+ "$source": "projectName"
37
+ }
26
38
  }
27
39
  },
28
- "required": ["name", "project"]
40
+ "required": [
41
+ "name",
42
+ "project"
43
+ ]
29
44
  }
@@ -1,59 +1,22 @@
1
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-space-2xs: 8px;
12
- --semmet-space-xs: 16px;
13
- --semmet-target-lg: 48px;
14
- --semmet-motion: 160ms cubic-bezier(0.2, 0, 0, 1);
15
-
16
2
  display: block;
17
- font-family: Roboto, system-ui, -apple-system, 'Segoe UI', sans-serif;
18
- font-size: 0.9375rem;
19
3
  }
20
4
 
21
5
  .semmet-input-group__label {
22
6
  display: inline-block;
23
- margin-block-end: var(--semmet-space-2xs);
24
- color: var(--semmet-color-on-surface);
25
- font-weight: 650;
26
- }
27
-
28
- .semmet-input-group__required {
29
- color: var(--semmet-color-danger);
30
7
  }
31
8
 
32
9
  .semmet-input-group {
33
10
  display: flex;
34
11
  align-items: stretch;
35
12
  width: 100%;
36
- min-height: var(--semmet-target-lg);
37
- border: 1px solid var(--semmet-color-outline);
38
- border-radius: var(--semmet-radius-sm);
39
- overflow: hidden;
40
- color: var(--semmet-color-on-surface);
41
- background: var(--semmet-color-surface);
42
- transition:
43
- border-color var(--semmet-motion),
44
- box-shadow var(--semmet-motion),
45
- opacity var(--semmet-motion);
46
13
  }
47
14
 
48
15
  .semmet-input-group:focus-within {
49
- outline: 2px solid var(--semmet-color-primary);
16
+ outline: 2px solid currentColor;
50
17
  outline-offset: 2px;
51
18
  }
52
19
 
53
- .semmet-input-group--invalid {
54
- border-color: var(--semmet-color-danger);
55
- }
56
-
57
20
  .semmet-input-group--disabled {
58
21
  opacity: 0.58;
59
22
  }
@@ -63,29 +26,15 @@
63
26
  display: inline-flex;
64
27
  align-items: center;
65
28
  justify-content: center;
66
- padding: 0 var(--semmet-space-xs);
67
29
  white-space: nowrap;
68
30
  }
69
31
 
70
- .semmet-input-group__addon {
71
- color: var(--semmet-color-on-surface-variant);
72
- background: var(--semmet-color-surface-muted);
73
- border-inline-end: 1px solid var(--semmet-color-outline);
74
- }
75
-
76
- .semmet-input-group__control + .semmet-input-group__addon {
77
- border-inline-start: 1px solid var(--semmet-color-outline);
78
- border-inline-end: 0;
79
- }
80
-
81
32
  .semmet-input-group__control {
82
33
  min-width: 0;
83
34
  flex: 1 1 auto;
84
- border: 0;
85
35
  color: inherit;
86
36
  background: transparent;
87
37
  font: inherit;
88
- padding: var(--semmet-space-2xs) var(--semmet-space-xs);
89
38
  }
90
39
 
91
40
  .semmet-input-group__control:focus {
@@ -93,12 +42,7 @@
93
42
  }
94
43
 
95
44
  .semmet-input-group__button {
96
- border: 0;
97
- border-inline-start: 1px solid var(--semmet-color-outline);
98
- color: var(--semmet-color-on-primary);
99
- background: var(--semmet-color-primary);
100
45
  font: inherit;
101
- font-weight: 650;
102
46
  cursor: pointer;
103
47
  }
104
48
 
@@ -108,15 +52,5 @@
108
52
 
109
53
  .semmet-input-group__hint,
110
54
  .semmet-input-group__error {
111
- margin: var(--semmet-space-2xs) 0 0;
112
- font-size: 0.875rem;
113
- line-height: 1.4;
114
- }
115
-
116
- .semmet-input-group__hint {
117
- color: var(--semmet-color-on-surface-variant);
118
- }
119
-
120
- .semmet-input-group__error {
121
- color: var(--semmet-color-danger);
55
+ margin: 0;
122
56
  }
@@ -1,9 +1,13 @@
1
- <!-- Semmet Angular: Input Group - native input with addon text announced through aria-describedby. -->
1
+ <!-- Semmet Angular: Input Group - native input with projected addons announced through aria-describedby. -->
2
2
  <!--
3
3
  Usage from a parent component template:
4
- Import <%= classify(name) %> in that parent component.
4
+ Import <%= classify(name) %>, <%= classify(name) %>Prefix, and <%= classify(name) %>Suffix in that parent component.
5
5
 
6
- <<%= selector %> label="Website" prefix="https://" suffix=".com" [(value)]="website" />
6
+ <<%= selector %> label="Search" type="search" [(value)]="query">
7
+ <ng-template <%= camelize(name) %>Prefix>
8
+ <svg aria-hidden="true" viewBox="0 0 20 20"><path d="M8.5 3a5.5 5.5 0 0 1 4.3 8.9l3.15 3.15-1.4 1.4-3.15-3.15A5.5 5.5 0 1 1 8.5 3Zm0 2a3.5 3.5 0 1 0 0 7 3.5 3.5 0 0 0 0-7Z" /></svg>
9
+ </ng-template>
10
+ </<%= selector %>>
7
11
  -->
8
12
  <label class="semmet-input-group__label" [for]="instanceId">
9
13
  {{ label() }}
@@ -18,7 +22,9 @@ Import <%= classify(name) %> in that parent component.
18
22
  >
19
23
  @if (hasPrefix()) {
20
24
  <span class="semmet-input-group__addon" [id]="instanceId + '-prefix'">
21
- {{ prefix() }}
25
+ @if (prefix(); as prefixSlot) {
26
+ <ng-container [ngTemplateOutlet]="prefixSlot.templateRef" />
27
+ }
22
28
  </span>
23
29
  }
24
30
  <input
@@ -35,7 +41,9 @@ Import <%= classify(name) %> in that parent component.
35
41
  />
36
42
  @if (hasSuffix()) {
37
43
  <span class="semmet-input-group__addon" [id]="instanceId + '-suffix'">
38
- {{ suffix() }}
44
+ @if (suffix(); as suffixSlot) {
45
+ <ng-container [ngTemplateOutlet]="suffixSlot.templateRef" />
46
+ }
39
47
  </span>
40
48
  }
41
49
  @if (hasAction()) {
@@ -1,23 +1,38 @@
1
- import { Component, booleanAttribute, computed, input, model, output } from '@angular/core';
1
+ import { NgTemplateOutlet } from '@angular/common';
2
+ import { Component, Directive, TemplateRef, booleanAttribute, computed, contentChild, inject, input, model, output } from '@angular/core';
2
3
 
3
4
  type <%= classify(name) %>InputType = 'text' | 'email' | 'search' | 'tel' | 'url';
4
5
 
5
6
  let nextId = 0;
6
7
 
8
+ @Directive({
9
+ selector: 'ng-template[<%= camelize(name) %>Prefix]',
10
+ })
11
+ export class <%= classify(name) %>Prefix {
12
+ readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);
13
+ }
14
+
15
+ @Directive({
16
+ selector: 'ng-template[<%= camelize(name) %>Suffix]',
17
+ })
18
+ export class <%= classify(name) %>Suffix {
19
+ readonly templateRef = inject<TemplateRef<unknown>>(TemplateRef);
20
+ }
21
+
7
22
  @Component({
8
23
  selector: '<%= selector %>',
9
- imports: [],
24
+ imports: [NgTemplateOutlet],
10
25
  templateUrl: './<%= dasherize(name) %>.html',
11
26
  styleUrl: './<%= dasherize(name) %>.css',
12
27
  })
13
28
  export class <%= classify(name) %> {
29
+ protected readonly prefix = contentChild(<%= classify(name) %>Prefix);
30
+ protected readonly suffix = contentChild(<%= classify(name) %>Suffix);
14
31
  protected readonly instanceId = `<%= dasherize(name) %>-${nextId++}`;
15
32
 
16
33
  readonly label = input('Website');
17
34
  readonly type = input<<%= classify(name) %>InputType>('url');
18
35
  readonly placeholder = input('example');
19
- readonly prefix = input('https://');
20
- readonly suffix = input('.com');
21
36
  readonly buttonLabel = input('Apply');
22
37
  readonly hint = input('Use a short, memorable address.');
23
38
  readonly errorMessage = input('');
@@ -26,8 +41,8 @@ export class <%= classify(name) %> {
26
41
  readonly value = model('');
27
42
  readonly buttonClick = output<MouseEvent>();
28
43
 
29
- protected readonly hasPrefix = computed(() => this.prefix().trim().length > 0);
30
- protected readonly hasSuffix = computed(() => this.suffix().trim().length > 0);
44
+ protected readonly hasPrefix = computed(() => Boolean(this.prefix()));
45
+ protected readonly hasSuffix = computed(() => Boolean(this.suffix()));
31
46
  protected readonly hasAction = computed(() => this.buttonLabel().trim().length > 0);
32
47
  protected readonly hasError = computed(() => this.errorMessage().trim().length > 0);
33
48
 
@@ -9,21 +9,36 @@
9
9
  "name": {
10
10
  "type": "string",
11
11
  "description": "The name for the new component. Used to derive the class name, selector, and file names.",
12
- "$default": { "$source": "argv", "index": 0 },
12
+ "$default": {
13
+ "$source": "argv",
14
+ "index": 0
15
+ },
13
16
  "x-prompt": "What name would you like to use for the input group component?"
14
17
  },
18
+ "e2e": {
19
+ "type": "boolean",
20
+ "default": false,
21
+ "description": "Generate a Playwright e2e spec for this component. Requires Playwright to be installed/configured in the consuming app."
22
+ },
15
23
  "path": {
16
24
  "type": "string",
17
25
  "format": "path",
18
- "$default": { "$source": "workingDirectory" },
26
+ "$default": {
27
+ "$source": "workingDirectory"
28
+ },
19
29
  "description": "The path where the component files should be created, relative to the workspace root. Defaults to the project's source root.",
20
30
  "visible": false
21
31
  },
22
32
  "project": {
23
33
  "type": "string",
24
34
  "description": "The name of the project to add the component to.",
25
- "$default": { "$source": "projectName" }
35
+ "$default": {
36
+ "$source": "projectName"
37
+ }
26
38
  }
27
39
  },
28
- "required": ["name", "project"]
40
+ "required": [
41
+ "name",
42
+ "project"
43
+ ]
29
44
  }