semmet-angular 0.11.0 → 0.13.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 (155) hide show
  1. package/README.md +15 -5
  2. package/package.json +1 -1
  3. package/src/alert/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  4. package/src/alert-dialog/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  5. package/src/badge/files/__name@dasherize__/__name@dasherize__.css.template +102 -0
  6. package/src/badge/files/__name@dasherize__/__name@dasherize__.html.template +21 -0
  7. package/src/badge/files/__name@dasherize__/__name@dasherize__.ts.template +43 -0
  8. package/src/badge/index.d.ts +1 -0
  9. package/src/badge/index.js +6 -0
  10. package/src/badge/index.js.map +1 -0
  11. package/src/badge/index.ts +3 -0
  12. package/src/badge/schema.d.ts +1 -0
  13. package/src/badge/schema.js +3 -0
  14. package/src/badge/schema.js.map +1 -0
  15. package/src/badge/schema.json +29 -0
  16. package/src/badge/schema.ts +1 -0
  17. package/src/breadcrumb/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  18. package/src/button/files/__name@dasherize__/__name@dasherize__.html.template +7 -0
  19. package/src/button-group/files/__name@dasherize__/__name@dasherize__.css.template +93 -0
  20. package/src/button-group/files/__name@dasherize__/__name@dasherize__.html.template +26 -0
  21. package/src/button-group/files/__name@dasherize__/__name@dasherize__.ts.template +85 -0
  22. package/src/button-group/index.d.ts +1 -0
  23. package/src/button-group/index.js +6 -0
  24. package/src/button-group/index.js.map +1 -0
  25. package/src/button-group/index.ts +3 -0
  26. package/src/button-group/schema.d.ts +1 -0
  27. package/src/button-group/schema.js +3 -0
  28. package/src/button-group/schema.js.map +1 -0
  29. package/src/button-group/schema.json +29 -0
  30. package/src/button-group/schema.ts +1 -0
  31. package/src/card/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  32. package/src/checkbox/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  33. package/src/close-button/files/__name@dasherize__/__name@dasherize__.css.template +105 -0
  34. package/src/close-button/files/__name@dasherize__/__name@dasherize__.html.template +17 -0
  35. package/src/close-button/files/__name@dasherize__/__name@dasherize__.ts.template +38 -0
  36. package/src/close-button/index.d.ts +1 -0
  37. package/src/close-button/index.js +6 -0
  38. package/src/close-button/index.js.map +1 -0
  39. package/src/close-button/index.ts +3 -0
  40. package/src/close-button/schema.d.ts +1 -0
  41. package/src/close-button/schema.js +3 -0
  42. package/src/close-button/schema.js.map +1 -0
  43. package/src/close-button/schema.json +29 -0
  44. package/src/close-button/schema.ts +1 -0
  45. package/src/collection.json +50 -0
  46. package/src/combobox/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  47. package/src/dialog/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  48. package/src/form/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  49. package/src/input/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  50. package/src/input-group/files/__name@dasherize__/__name@dasherize__.css.template +119 -0
  51. package/src/input-group/files/__name@dasherize__/__name@dasherize__.html.template +57 -0
  52. package/src/input-group/files/__name@dasherize__/__name@dasherize__.ts.template +64 -0
  53. package/src/input-group/index.d.ts +1 -0
  54. package/src/input-group/index.js +6 -0
  55. package/src/input-group/index.js.map +1 -0
  56. package/src/input-group/index.ts +3 -0
  57. package/src/input-group/schema.d.ts +1 -0
  58. package/src/input-group/schema.js +3 -0
  59. package/src/input-group/schema.js.map +1 -0
  60. package/src/input-group/schema.json +29 -0
  61. package/src/input-group/schema.ts +1 -0
  62. package/src/landmarks/files/__name@dasherize__/__name@dasherize__.html.template +12 -0
  63. package/src/list-group/files/__name@dasherize__/__name@dasherize__.css.template +121 -0
  64. package/src/list-group/files/__name@dasherize__/__name@dasherize__.html.template +53 -0
  65. package/src/list-group/files/__name@dasherize__/__name@dasherize__.ts.template +55 -0
  66. package/src/list-group/index.d.ts +1 -0
  67. package/src/list-group/index.js +6 -0
  68. package/src/list-group/index.js.map +1 -0
  69. package/src/list-group/index.ts +3 -0
  70. package/src/list-group/schema.d.ts +1 -0
  71. package/src/list-group/schema.js +3 -0
  72. package/src/list-group/schema.js.map +1 -0
  73. package/src/list-group/schema.json +29 -0
  74. package/src/list-group/schema.ts +1 -0
  75. package/src/listbox/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  76. package/src/menu-button/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  77. package/src/meter/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  78. package/src/navbar/files/__name@dasherize__/__name@dasherize__.css.template +158 -0
  79. package/src/navbar/files/__name@dasherize__/__name@dasherize__.html.template +42 -0
  80. package/src/navbar/files/__name@dasherize__/__name@dasherize__.ts.template +47 -0
  81. package/src/navbar/index.d.ts +1 -0
  82. package/src/navbar/index.js +6 -0
  83. package/src/navbar/index.js.map +1 -0
  84. package/src/navbar/index.ts +3 -0
  85. package/src/navbar/schema.d.ts +1 -0
  86. package/src/navbar/schema.js +3 -0
  87. package/src/navbar/schema.js.map +1 -0
  88. package/src/navbar/schema.json +29 -0
  89. package/src/navbar/schema.ts +1 -0
  90. package/src/navigation-menu/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  91. package/src/offcanvas/files/__name@dasherize__/__name@dasherize__.css.template +180 -0
  92. package/src/offcanvas/files/__name@dasherize__/__name@dasherize__.html.template +60 -0
  93. package/src/offcanvas/files/__name@dasherize__/__name@dasherize__.ts.template +96 -0
  94. package/src/offcanvas/index.d.ts +1 -0
  95. package/src/offcanvas/index.js +6 -0
  96. package/src/offcanvas/index.js.map +1 -0
  97. package/src/offcanvas/index.ts +3 -0
  98. package/src/offcanvas/schema.d.ts +1 -0
  99. package/src/offcanvas/schema.js +3 -0
  100. package/src/offcanvas/schema.js.map +1 -0
  101. package/src/offcanvas/schema.json +29 -0
  102. package/src/offcanvas/schema.ts +1 -0
  103. package/src/pagination/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  104. package/src/popover/files/__name@dasherize__/__name@dasherize__.css.template +156 -0
  105. package/src/popover/files/__name@dasherize__/__name@dasherize__.html.template +48 -0
  106. package/src/popover/files/__name@dasherize__/__name@dasherize__.ts.template +65 -0
  107. package/src/popover/index.d.ts +1 -0
  108. package/src/popover/index.js +6 -0
  109. package/src/popover/index.js.map +1 -0
  110. package/src/popover/index.ts +3 -0
  111. package/src/popover/schema.d.ts +1 -0
  112. package/src/popover/schema.js +3 -0
  113. package/src/popover/schema.js.map +1 -0
  114. package/src/popover/schema.json +29 -0
  115. package/src/popover/schema.ts +1 -0
  116. package/src/progress-bar/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  117. package/src/radio-group/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  118. package/src/select/files/__name@dasherize__/__name@dasherize__.css.template +92 -0
  119. package/src/select/files/__name@dasherize__/__name@dasherize__.html.template +39 -0
  120. package/src/select/files/__name@dasherize__/__name@dasherize__.ts.template +51 -0
  121. package/src/select/index.d.ts +1 -0
  122. package/src/select/index.js +6 -0
  123. package/src/select/index.js.map +1 -0
  124. package/src/select/index.ts +3 -0
  125. package/src/select/schema.d.ts +1 -0
  126. package/src/select/schema.js +3 -0
  127. package/src/select/schema.js.map +1 -0
  128. package/src/select/schema.json +29 -0
  129. package/src/select/schema.ts +1 -0
  130. package/src/skeleton/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  131. package/src/skip-link/files/__name@dasherize__/__name@dasherize__.html.template +7 -0
  132. package/src/slider/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  133. package/src/spinbutton/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  134. package/src/spinner/files/__name@dasherize__/__name@dasherize__.css.template +85 -0
  135. package/src/spinner/files/__name@dasherize__/__name@dasherize__.html.template +25 -0
  136. package/src/spinner/files/__name@dasherize__/__name@dasherize__.ts.template +33 -0
  137. package/src/spinner/index.d.ts +1 -0
  138. package/src/spinner/index.js +6 -0
  139. package/src/spinner/index.js.map +1 -0
  140. package/src/spinner/index.ts +3 -0
  141. package/src/spinner/schema.d.ts +1 -0
  142. package/src/spinner/schema.js +3 -0
  143. package/src/spinner/schema.js.map +1 -0
  144. package/src/spinner/schema.json +29 -0
  145. package/src/spinner/schema.ts +1 -0
  146. package/src/switch/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  147. package/src/table/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  148. package/src/textarea/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  149. package/src/toast/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  150. package/src/toolbar/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  151. package/src/tooltip/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  152. package/src/tree-view/files/__name@dasherize__/__name@dasherize__.html.template +6 -0
  153. package/src/utils/generate-component.d.ts +1 -1
  154. package/src/utils/generate-component.js +1 -1
  155. package/src/utils/generate-component.ts +1 -1
@@ -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';
@@ -1,4 +1,10 @@
1
1
  <!-- Semmet Angular: Navigation Menu (Disclosure) — https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-navigation/ -->
2
+ <!--
3
+ Usage from a parent component template:
4
+ Import <%= classify(name) %> in that parent component.
5
+
6
+ <<%= selector %> />
7
+ -->
2
8
  <!-- Deliberately avoids the ARIA menu/menuitem roles — APG reserves those for app-style menus. -->
3
9
  <nav aria-label="Main">
4
10
  <ul>
@@ -0,0 +1,180 @@
1
+ :host {
2
+ --semmet-color-primary: #18181b;
3
+ --semmet-color-on-primary: #ffffff;
4
+ --semmet-color-surface: #ffffff;
5
+ --semmet-color-backdrop: rgba(24, 24, 27, 0.44);
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-3: 0 20px 48px rgba(20, 24, 23, 0.22), 0 8px 20px rgba(20, 24, 23, 0.14);
11
+ --semmet-motion: 220ms cubic-bezier(0.2, 0, 0, 1);
12
+
13
+ display: inline-block;
14
+ font-family: Roboto, system-ui, -apple-system, 'Segoe UI', sans-serif;
15
+ }
16
+
17
+ .semmet-offcanvas__trigger {
18
+ min-height: 2.5rem;
19
+ padding: 0.5rem 1rem;
20
+ border: 1px solid transparent;
21
+ border-radius: var(--semmet-radius-md);
22
+ color: var(--semmet-color-on-primary);
23
+ background: var(--semmet-color-primary);
24
+ font: inherit;
25
+ font-weight: 650;
26
+ cursor: pointer;
27
+ }
28
+
29
+ .semmet-offcanvas__trigger:focus-visible,
30
+ .semmet-offcanvas__close:focus-visible {
31
+ outline: 2px solid var(--semmet-color-primary);
32
+ outline-offset: 2px;
33
+ }
34
+
35
+ .semmet-offcanvas__backdrop {
36
+ position: fixed;
37
+ inset: 0;
38
+ z-index: 1000;
39
+ background: var(--semmet-color-backdrop);
40
+ }
41
+
42
+ .semmet-offcanvas__panel {
43
+ position: fixed;
44
+ z-index: 1001;
45
+ display: grid;
46
+ grid-template-rows: auto 1fr;
47
+ color: var(--semmet-color-on-surface);
48
+ background: var(--semmet-color-surface);
49
+ box-shadow: var(--semmet-elevation-3);
50
+ animation: semmet-offcanvas-enter var(--semmet-motion);
51
+ }
52
+
53
+ .semmet-offcanvas__panel--start,
54
+ .semmet-offcanvas__panel--end {
55
+ top: 0;
56
+ bottom: 0;
57
+ width: min(26rem, 92vw);
58
+ }
59
+
60
+ .semmet-offcanvas__panel--start {
61
+ left: 0;
62
+ }
63
+
64
+ .semmet-offcanvas__panel--end {
65
+ right: 0;
66
+ }
67
+
68
+ .semmet-offcanvas__panel--top,
69
+ .semmet-offcanvas__panel--bottom {
70
+ left: 0;
71
+ right: 0;
72
+ max-height: min(32rem, 82vh);
73
+ }
74
+
75
+ .semmet-offcanvas__panel--top {
76
+ top: 0;
77
+ }
78
+
79
+ .semmet-offcanvas__panel--bottom {
80
+ bottom: 0;
81
+ }
82
+
83
+ .semmet-offcanvas__header {
84
+ display: flex;
85
+ align-items: start;
86
+ justify-content: space-between;
87
+ gap: 1rem;
88
+ padding: 1.25rem;
89
+ border-block-end: 1px solid var(--semmet-color-outline);
90
+ }
91
+
92
+ .semmet-offcanvas__title,
93
+ .semmet-offcanvas__description,
94
+ .semmet-offcanvas__placeholder {
95
+ margin: 0;
96
+ }
97
+
98
+ .semmet-offcanvas__title {
99
+ font-size: 1.125rem;
100
+ line-height: 1.25;
101
+ }
102
+
103
+ .semmet-offcanvas__description {
104
+ margin-block-start: 0.375rem;
105
+ color: var(--semmet-color-on-surface-variant);
106
+ line-height: 1.4;
107
+ }
108
+
109
+ .semmet-offcanvas__close {
110
+ position: relative;
111
+ display: inline-flex;
112
+ align-items: center;
113
+ justify-content: center;
114
+ flex: 0 0 auto;
115
+ width: 2.5rem;
116
+ height: 2.5rem;
117
+ border: 0;
118
+ border-radius: 999px;
119
+ color: inherit;
120
+ background: transparent;
121
+ cursor: pointer;
122
+ }
123
+
124
+ .semmet-offcanvas__close:hover {
125
+ background: #f4f4f5;
126
+ }
127
+
128
+ .semmet-offcanvas__close span,
129
+ .semmet-offcanvas__close span::before,
130
+ .semmet-offcanvas__close span::after {
131
+ display: block;
132
+ width: 1rem;
133
+ height: 0.125rem;
134
+ }
135
+
136
+ .semmet-offcanvas__close span {
137
+ position: relative;
138
+ }
139
+
140
+ .semmet-offcanvas__close span::before,
141
+ .semmet-offcanvas__close span::after {
142
+ position: absolute;
143
+ top: 50%;
144
+ left: 0;
145
+ border-radius: 999px;
146
+ background: currentColor;
147
+ content: '';
148
+ }
149
+
150
+ .semmet-offcanvas__close span::before {
151
+ transform: rotate(45deg);
152
+ }
153
+
154
+ .semmet-offcanvas__close span::after {
155
+ transform: rotate(-45deg);
156
+ }
157
+
158
+ .semmet-offcanvas__body {
159
+ min-height: 0;
160
+ overflow: auto;
161
+ padding: 1.25rem;
162
+ }
163
+
164
+ .semmet-offcanvas__placeholder {
165
+ color: var(--semmet-color-on-surface-variant);
166
+ line-height: 1.5;
167
+ }
168
+
169
+ @keyframes semmet-offcanvas-enter {
170
+ from {
171
+ opacity: 0;
172
+ transform: translateX(1rem);
173
+ }
174
+ }
175
+
176
+ @media (prefers-reduced-motion: reduce) {
177
+ .semmet-offcanvas__panel {
178
+ animation-duration: 1ms;
179
+ }
180
+ }
@@ -0,0 +1,60 @@
1
+ <!-- Semmet Angular: Offcanvas - modal drawer with dialog semantics and focus management. -->
2
+ <!--
3
+ Usage from a parent component template:
4
+ Import <%= classify(name) %> in that parent component.
5
+
6
+ <<%= selector %> triggerLabel="Open filters" title="Filters">
7
+ <app-filter-panel />
8
+ </<%= selector %>>
9
+ -->
10
+ <button
11
+ type="button"
12
+ class="semmet-offcanvas__trigger"
13
+ [attr.aria-controls]="instanceId + '-panel'"
14
+ [attr.aria-expanded]="isOpen()"
15
+ (click)="open($event)"
16
+ >
17
+ {{ triggerLabel() }}
18
+ </button>
19
+
20
+ @if (isOpen()) {
21
+ @if (backdrop()) {
22
+ <div class="semmet-offcanvas__backdrop" (click)="handleBackdropClick()"></div>
23
+ }
24
+ <section
25
+ #panel
26
+ tabindex="-1"
27
+ role="dialog"
28
+ aria-modal="true"
29
+ [id]="instanceId + '-panel'"
30
+ [class]="panelClasses()"
31
+ [attr.aria-labelledby]="instanceId + '-title'"
32
+ [attr.aria-describedby]="description() ? instanceId + '-description' : null"
33
+ (keydown)="handlePanelKeydown($event)"
34
+ >
35
+ <header class="semmet-offcanvas__header">
36
+ <div>
37
+ <h2 class="semmet-offcanvas__title" [id]="instanceId + '-title'">{{ title() }}</h2>
38
+ @if (description()) {
39
+ <p class="semmet-offcanvas__description" [id]="instanceId + '-description'">
40
+ {{ description() }}
41
+ </p>
42
+ }
43
+ </div>
44
+ <button
45
+ type="button"
46
+ class="semmet-offcanvas__close"
47
+ [attr.aria-label]="closeLabel()"
48
+ (click)="close()"
49
+ >
50
+ <span aria-hidden="true"></span>
51
+ </button>
52
+ </header>
53
+ <div class="semmet-offcanvas__body">
54
+ <ng-content></ng-content>
55
+ <p class="semmet-offcanvas__placeholder">
56
+ Replace this with filters, navigation, or contextual controls.
57
+ </p>
58
+ </div>
59
+ </section>
60
+ }
@@ -0,0 +1,96 @@
1
+ import { Component, ElementRef, Injector, afterNextRender, booleanAttribute, computed, inject, input, signal, viewChild } from '@angular/core';
2
+
3
+ const FOCUSABLE_SELECTOR =
4
+ 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
5
+
6
+ type <%= classify(name) %>Placement = 'start' | 'end' | 'top' | 'bottom';
7
+
8
+ let nextId = 0;
9
+
10
+ @Component({
11
+ selector: '<%= selector %>',
12
+ imports: [],
13
+ templateUrl: './<%= dasherize(name) %>.html',
14
+ styleUrl: './<%= dasherize(name) %>.css',
15
+ })
16
+ export class <%= classify(name) %> {
17
+ private readonly injector = inject(Injector);
18
+ private readonly panelRef = viewChild<ElementRef<HTMLElement>>('panel');
19
+ private triggerElement: HTMLElement | null = null;
20
+
21
+ protected readonly instanceId = `<%= dasherize(name) %>-${nextId++}`;
22
+ protected readonly isOpen = signal(false);
23
+
24
+ readonly triggerLabel = input('Open panel');
25
+ readonly title = input('Panel title');
26
+ readonly description = input('Use this panel for secondary actions, filters, or contextual content.');
27
+ readonly closeLabel = input('Close panel');
28
+ readonly placement = input<<%= classify(name) %>Placement>('end');
29
+ readonly backdrop = input(true, { transform: booleanAttribute });
30
+ readonly closeOnBackdrop = input(true, { transform: booleanAttribute });
31
+
32
+ protected readonly panelClasses = computed(() => [
33
+ 'semmet-offcanvas__panel',
34
+ `semmet-offcanvas__panel--${this.placement()}`,
35
+ ].join(' '));
36
+
37
+ protected open(event: MouseEvent): void {
38
+ this.triggerElement = event.currentTarget as HTMLElement;
39
+ this.isOpen.set(true);
40
+ afterNextRender(() => this.focusFirstElement(), { injector: this.injector });
41
+ }
42
+
43
+ protected close(): void {
44
+ this.isOpen.set(false);
45
+ this.triggerElement?.focus();
46
+ }
47
+
48
+ protected handleBackdropClick(): void {
49
+ if (this.closeOnBackdrop()) {
50
+ this.close();
51
+ }
52
+ }
53
+
54
+ protected handlePanelKeydown(event: KeyboardEvent): void {
55
+ if (event.key === 'Escape') {
56
+ event.preventDefault();
57
+ this.close();
58
+ return;
59
+ }
60
+
61
+ if (event.key === 'Tab') {
62
+ this.trapFocus(event);
63
+ }
64
+ }
65
+
66
+ private focusFirstElement(): void {
67
+ const panel = this.panelRef()?.nativeElement;
68
+ const firstFocusable = panel?.querySelector<HTMLElement>(FOCUSABLE_SELECTOR);
69
+ (firstFocusable ?? panel)?.focus();
70
+ }
71
+
72
+ // Keeps Tab/Shift+Tab cycling within the offcanvas while it behaves as a modal dialog.
73
+ private trapFocus(event: KeyboardEvent): void {
74
+ const panel = this.panelRef()?.nativeElement;
75
+ if (!panel) {
76
+ return;
77
+ }
78
+
79
+ const focusable = Array.from(panel.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));
80
+ if (focusable.length === 0) {
81
+ return;
82
+ }
83
+
84
+ const first = focusable[0];
85
+ const last = focusable[focusable.length - 1];
86
+ const active = panel.ownerDocument.activeElement;
87
+
88
+ if (event.shiftKey && active === first) {
89
+ event.preventDefault();
90
+ last.focus();
91
+ } else if (!event.shiftKey && active === last) {
92
+ event.preventDefault();
93
+ first.focus();
94
+ }
95
+ }
96
+ }
@@ -0,0 +1 @@
1
+ export declare const offcanvas: (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.offcanvas = void 0;
4
+ const generate_component_1 = require("../utils/generate-component");
5
+ exports.offcanvas = (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 offcanvas = 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": "SemmetAngularOffcanvas",
4
+ "title": "Semmet Angular Offcanvas Options Schema",
5
+ "type": "object",
6
+ "description": "Generates a standalone Angular Offcanvas component using modal dialog semantics, focus trap, Escape close, and focus restoration.",
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 offcanvas 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';
@@ -1,4 +1,10 @@
1
1
  <!-- Semmet Angular: Pagination — https://www.w3.org/TR/wai-aria-1.2/#aria-current -->
2
+ <!--
3
+ Usage from a parent component template:
4
+ Import <%= classify(name) %> in that parent component.
5
+
6
+ <<%= selector %> />
7
+ -->
2
8
  <nav aria-label="Pagination">
3
9
  <ul>
4
10
  <li>
@@ -0,0 +1,156 @@
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-2: 0 12px 28px rgba(20, 24, 23, 0.16), 0 4px 12px rgba(20, 24, 23, 0.1);
11
+ --semmet-motion: 160ms cubic-bezier(0.2, 0, 0, 1);
12
+
13
+ position: relative;
14
+ display: inline-block;
15
+ font-family: Roboto, system-ui, -apple-system, 'Segoe UI', sans-serif;
16
+ }
17
+
18
+ .semmet-popover__trigger {
19
+ min-height: 2.5rem;
20
+ padding: 0.5rem 1rem;
21
+ border: 1px solid var(--semmet-color-outline);
22
+ border-radius: var(--semmet-radius-md);
23
+ color: var(--semmet-color-on-surface);
24
+ background: var(--semmet-color-surface);
25
+ font: inherit;
26
+ font-weight: 650;
27
+ cursor: pointer;
28
+ }
29
+
30
+ .semmet-popover__trigger:focus-visible,
31
+ .semmet-popover__close:focus-visible,
32
+ .semmet-popover__panel:focus-visible {
33
+ outline: 2px solid var(--semmet-color-primary);
34
+ outline-offset: 2px;
35
+ }
36
+
37
+ .semmet-popover__panel {
38
+ position: absolute;
39
+ z-index: 20;
40
+ width: min(20rem, calc(100vw - 2rem));
41
+ padding: 1rem;
42
+ border: 1px solid var(--semmet-color-outline);
43
+ border-radius: var(--semmet-radius-md);
44
+ color: var(--semmet-color-on-surface);
45
+ background: var(--semmet-color-surface);
46
+ box-shadow: var(--semmet-elevation-2);
47
+ animation: semmet-popover-enter var(--semmet-motion);
48
+ }
49
+
50
+ .semmet-popover__panel--top {
51
+ left: 50%;
52
+ bottom: calc(100% + 0.5rem);
53
+ transform: translateX(-50%);
54
+ }
55
+
56
+ .semmet-popover__panel--right {
57
+ top: 50%;
58
+ left: calc(100% + 0.5rem);
59
+ transform: translateY(-50%);
60
+ }
61
+
62
+ .semmet-popover__panel--bottom {
63
+ top: calc(100% + 0.5rem);
64
+ left: 50%;
65
+ transform: translateX(-50%);
66
+ }
67
+
68
+ .semmet-popover__panel--left {
69
+ top: 50%;
70
+ right: calc(100% + 0.5rem);
71
+ transform: translateY(-50%);
72
+ }
73
+
74
+ .semmet-popover__header {
75
+ display: flex;
76
+ align-items: start;
77
+ justify-content: space-between;
78
+ gap: 1rem;
79
+ }
80
+
81
+ .semmet-popover__title,
82
+ .semmet-popover__body p {
83
+ margin: 0;
84
+ }
85
+
86
+ .semmet-popover__title {
87
+ font-size: 1rem;
88
+ line-height: 1.3;
89
+ }
90
+
91
+ .semmet-popover__body {
92
+ margin-block-start: 0.625rem;
93
+ color: var(--semmet-color-on-surface-variant);
94
+ line-height: 1.5;
95
+ }
96
+
97
+ .semmet-popover__close {
98
+ position: relative;
99
+ display: inline-flex;
100
+ align-items: center;
101
+ justify-content: center;
102
+ flex: 0 0 auto;
103
+ width: 2rem;
104
+ height: 2rem;
105
+ border: 0;
106
+ border-radius: 999px;
107
+ color: inherit;
108
+ background: transparent;
109
+ cursor: pointer;
110
+ }
111
+
112
+ .semmet-popover__close:hover {
113
+ background: var(--semmet-color-surface-muted);
114
+ }
115
+
116
+ .semmet-popover__close span,
117
+ .semmet-popover__close span::before,
118
+ .semmet-popover__close span::after {
119
+ display: block;
120
+ width: 0.875rem;
121
+ height: 0.125rem;
122
+ }
123
+
124
+ .semmet-popover__close span {
125
+ position: relative;
126
+ }
127
+
128
+ .semmet-popover__close span::before,
129
+ .semmet-popover__close span::after {
130
+ position: absolute;
131
+ top: 50%;
132
+ left: 0;
133
+ border-radius: 999px;
134
+ background: currentColor;
135
+ content: '';
136
+ }
137
+
138
+ .semmet-popover__close span::before {
139
+ transform: rotate(45deg);
140
+ }
141
+
142
+ .semmet-popover__close span::after {
143
+ transform: rotate(-45deg);
144
+ }
145
+
146
+ @keyframes semmet-popover-enter {
147
+ from {
148
+ opacity: 0;
149
+ }
150
+ }
151
+
152
+ @media (prefers-reduced-motion: reduce) {
153
+ .semmet-popover__panel {
154
+ animation-duration: 1ms;
155
+ }
156
+ }
@@ -0,0 +1,48 @@
1
+ <!-- Semmet Angular: Popover - disclosure trigger for short, contextual non-modal content. -->
2
+ <!--
3
+ Usage from a parent component template:
4
+ Import <%= classify(name) %> in that parent component.
5
+
6
+ <<%= selector %> triggerLabel="More information" title="Helpful context">
7
+ <a routerLink="/help">Read the guide</a>
8
+ </<%= selector %>>
9
+ -->
10
+ <button
11
+ type="button"
12
+ class="semmet-popover__trigger"
13
+ aria-haspopup="dialog"
14
+ [attr.aria-expanded]="isOpen()"
15
+ [attr.aria-controls]="instanceId + '-panel'"
16
+ (click)="toggle()"
17
+ (keydown)="handleKeydown($event)"
18
+ >
19
+ {{ triggerLabel() }}
20
+ </button>
21
+
22
+ @if (isOpen()) {
23
+ <section
24
+ #popover
25
+ tabindex="-1"
26
+ role="dialog"
27
+ [id]="instanceId + '-panel'"
28
+ [class]="popoverClasses()"
29
+ [attr.aria-labelledby]="instanceId + '-title'"
30
+ (keydown)="handleKeydown($event)"
31
+ >
32
+ <div class="semmet-popover__header">
33
+ <h2 class="semmet-popover__title" [id]="instanceId + '-title'">{{ title() }}</h2>
34
+ <button
35
+ type="button"
36
+ class="semmet-popover__close"
37
+ [attr.aria-label]="closeLabel()"
38
+ (click)="close()"
39
+ >
40
+ <span aria-hidden="true"></span>
41
+ </button>
42
+ </div>
43
+ <div class="semmet-popover__body">
44
+ <p>{{ content() }}</p>
45
+ <ng-content></ng-content>
46
+ </div>
47
+ </section>
48
+ }