semmet-angular 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -5
- package/package.json +1 -1
- package/src/badge/files/__name@dasherize__/__name@dasherize__.css.template +102 -0
- package/src/badge/files/__name@dasherize__/__name@dasherize__.html.template +14 -0
- package/src/badge/files/__name@dasherize__/__name@dasherize__.ts.template +43 -0
- package/src/badge/index.d.ts +1 -0
- package/src/badge/index.js +6 -0
- package/src/badge/index.js.map +1 -0
- package/src/badge/index.ts +3 -0
- package/src/badge/schema.d.ts +1 -0
- package/src/badge/schema.js +3 -0
- package/src/badge/schema.js.map +1 -0
- package/src/badge/schema.json +29 -0
- package/src/badge/schema.ts +1 -0
- package/src/button-group/files/__name@dasherize__/__name@dasherize__.css.template +93 -0
- package/src/button-group/files/__name@dasherize__/__name@dasherize__.html.template +20 -0
- package/src/button-group/files/__name@dasherize__/__name@dasherize__.ts.template +85 -0
- package/src/button-group/index.d.ts +1 -0
- package/src/button-group/index.js +6 -0
- package/src/button-group/index.js.map +1 -0
- package/src/button-group/index.ts +3 -0
- package/src/button-group/schema.d.ts +1 -0
- package/src/button-group/schema.js +3 -0
- package/src/button-group/schema.js.map +1 -0
- package/src/button-group/schema.json +29 -0
- package/src/button-group/schema.ts +1 -0
- package/src/close-button/files/__name@dasherize__/__name@dasherize__.css.template +105 -0
- package/src/close-button/files/__name@dasherize__/__name@dasherize__.html.template +11 -0
- package/src/close-button/files/__name@dasherize__/__name@dasherize__.ts.template +38 -0
- package/src/close-button/index.d.ts +1 -0
- package/src/close-button/index.js +6 -0
- package/src/close-button/index.js.map +1 -0
- package/src/close-button/index.ts +3 -0
- package/src/close-button/schema.d.ts +1 -0
- package/src/close-button/schema.js +3 -0
- package/src/close-button/schema.js.map +1 -0
- package/src/close-button/schema.json +29 -0
- package/src/close-button/schema.ts +1 -0
- package/src/collection.json +50 -0
- package/src/input-group/files/__name@dasherize__/__name@dasherize__.css.template +119 -0
- package/src/input-group/files/__name@dasherize__/__name@dasherize__.html.template +51 -0
- package/src/input-group/files/__name@dasherize__/__name@dasherize__.ts.template +64 -0
- package/src/input-group/index.d.ts +1 -0
- package/src/input-group/index.js +6 -0
- package/src/input-group/index.js.map +1 -0
- package/src/input-group/index.ts +3 -0
- package/src/input-group/schema.d.ts +1 -0
- package/src/input-group/schema.js +3 -0
- package/src/input-group/schema.js.map +1 -0
- package/src/input-group/schema.json +29 -0
- package/src/input-group/schema.ts +1 -0
- package/src/list-group/files/__name@dasherize__/__name@dasherize__.css.template +121 -0
- package/src/list-group/files/__name@dasherize__/__name@dasherize__.html.template +47 -0
- package/src/list-group/files/__name@dasherize__/__name@dasherize__.ts.template +55 -0
- package/src/list-group/index.d.ts +1 -0
- package/src/list-group/index.js +6 -0
- package/src/list-group/index.js.map +1 -0
- package/src/list-group/index.ts +3 -0
- package/src/list-group/schema.d.ts +1 -0
- package/src/list-group/schema.js +3 -0
- package/src/list-group/schema.js.map +1 -0
- package/src/list-group/schema.json +29 -0
- package/src/list-group/schema.ts +1 -0
- package/src/navbar/files/__name@dasherize__/__name@dasherize__.css.template +158 -0
- package/src/navbar/files/__name@dasherize__/__name@dasherize__.html.template +36 -0
- package/src/navbar/files/__name@dasherize__/__name@dasherize__.ts.template +47 -0
- package/src/navbar/index.d.ts +1 -0
- package/src/navbar/index.js +6 -0
- package/src/navbar/index.js.map +1 -0
- package/src/navbar/index.ts +3 -0
- package/src/navbar/schema.d.ts +1 -0
- package/src/navbar/schema.js +3 -0
- package/src/navbar/schema.js.map +1 -0
- package/src/navbar/schema.json +29 -0
- package/src/navbar/schema.ts +1 -0
- package/src/offcanvas/files/__name@dasherize__/__name@dasherize__.css.template +180 -0
- package/src/offcanvas/files/__name@dasherize__/__name@dasherize__.html.template +52 -0
- package/src/offcanvas/files/__name@dasherize__/__name@dasherize__.ts.template +96 -0
- package/src/offcanvas/index.d.ts +1 -0
- package/src/offcanvas/index.js +6 -0
- package/src/offcanvas/index.js.map +1 -0
- package/src/offcanvas/index.ts +3 -0
- package/src/offcanvas/schema.d.ts +1 -0
- package/src/offcanvas/schema.js +3 -0
- package/src/offcanvas/schema.js.map +1 -0
- package/src/offcanvas/schema.json +29 -0
- package/src/offcanvas/schema.ts +1 -0
- package/src/popover/files/__name@dasherize__/__name@dasherize__.css.template +156 -0
- package/src/popover/files/__name@dasherize__/__name@dasherize__.html.template +40 -0
- package/src/popover/files/__name@dasherize__/__name@dasherize__.ts.template +65 -0
- package/src/popover/index.d.ts +1 -0
- package/src/popover/index.js +6 -0
- package/src/popover/index.js.map +1 -0
- package/src/popover/index.ts +3 -0
- package/src/popover/schema.d.ts +1 -0
- package/src/popover/schema.js +3 -0
- package/src/popover/schema.js.map +1 -0
- package/src/popover/schema.json +29 -0
- package/src/popover/schema.ts +1 -0
- package/src/select/files/__name@dasherize__/__name@dasherize__.css.template +92 -0
- package/src/select/files/__name@dasherize__/__name@dasherize__.html.template +33 -0
- package/src/select/files/__name@dasherize__/__name@dasherize__.ts.template +51 -0
- package/src/select/index.d.ts +1 -0
- package/src/select/index.js +6 -0
- package/src/select/index.js.map +1 -0
- package/src/select/index.ts +3 -0
- package/src/select/schema.d.ts +1 -0
- package/src/select/schema.js +3 -0
- package/src/select/schema.js.map +1 -0
- package/src/select/schema.json +29 -0
- package/src/select/schema.ts +1 -0
- package/src/spinner/files/__name@dasherize__/__name@dasherize__.css.template +85 -0
- package/src/spinner/files/__name@dasherize__/__name@dasherize__.html.template +18 -0
- package/src/spinner/files/__name@dasherize__/__name@dasherize__.ts.template +33 -0
- package/src/spinner/index.d.ts +1 -0
- package/src/spinner/index.js +6 -0
- package/src/spinner/index.js.map +1 -0
- package/src/spinner/index.ts +3 -0
- package/src/spinner/schema.d.ts +1 -0
- package/src/spinner/schema.js +3 -0
- package/src/spinner/schema.js.map +1 -0
- package/src/spinner/schema.json +29 -0
- package/src/spinner/schema.ts +1 -0
- package/src/utils/generate-component.d.ts +1 -1
- package/src/utils/generate-component.js +1 -1
- package/src/utils/generate-component.ts +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
-
```
|
|
13
|
+
```
|
|
14
14
|
npm install -D semmet-angular
|
|
15
15
|
ng generate semmet-angular:accordion my-faq
|
|
16
16
|
```
|
|
@@ -25,7 +25,7 @@ Some generated components are containers: they own the ARIA wiring, keyboard beh
|
|
|
25
25
|
|
|
26
26
|
Example after generating `faq`:
|
|
27
27
|
|
|
28
|
-
```
|
|
28
|
+
```
|
|
29
29
|
import { Component, signal } from '@angular/core';
|
|
30
30
|
import { Faq, FaqItem } from './faq/faq';
|
|
31
31
|
import { ProfilePanel } from './profile-panel/profile-panel';
|
|
@@ -43,7 +43,7 @@ export class App {
|
|
|
43
43
|
}
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
```
|
|
46
|
+
```
|
|
47
47
|
<app-faq>
|
|
48
48
|
<ng-template faqItem id="profile" title="Profile" [expanded]="true">
|
|
49
49
|
<app-profile-panel />
|
|
@@ -57,7 +57,7 @@ export class App {
|
|
|
57
57
|
|
|
58
58
|
The same pattern applies to `tabs`, `carousel`, and `disclosure`:
|
|
59
59
|
|
|
60
|
-
```
|
|
60
|
+
```
|
|
61
61
|
<app-settings-tabs>
|
|
62
62
|
<ng-template settingsTabsItem id="profile" label="Profile" [selected]="true">
|
|
63
63
|
<app-profile-panel />
|
|
@@ -79,7 +79,7 @@ The same pattern applies to `tabs`, `carousel`, and `disclosure`:
|
|
|
79
79
|
|
|
80
80
|
By default, projected panel/slide content is preserved after its first render. Set `[preserveContent]="false"` on the generated container to destroy inactive content when it closes or becomes inactive.
|
|
81
81
|
|
|
82
|
-
## Schematics (
|
|
82
|
+
## Schematics (43)
|
|
83
83
|
|
|
84
84
|
**Overlays & disclosure**
|
|
85
85
|
|
|
@@ -89,9 +89,13 @@ By default, projected panel/slide content is preserved after its first render. S
|
|
|
89
89
|
| `ng generate semmet-angular:disclosure <name>` | [Disclosure](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) | Click/Enter/Space toggle |
|
|
90
90
|
| `ng generate semmet-angular:dialog <name>` | [Dialog (Modal)](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/) | Focus moves in on open; Tab/Shift+Tab focus trap; Escape closes; focus returns to trigger on close |
|
|
91
91
|
| `ng generate semmet-angular:alert-dialog <name>` | [Alert Dialog](https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/) | Same focus trap as Dialog; initial focus on the least destructive action (Cancel) |
|
|
92
|
+
| `ng generate semmet-angular:offcanvas <name>` | Offcanvas / Drawer | Modal dialog semantics; focus moves in; Tab/Shift+Tab focus trap; Escape/backdrop close; focus returns to trigger |
|
|
93
|
+
| `ng generate semmet-angular:popover <name>` | Popover | Disclosure trigger with `aria-haspopup="dialog"`; Escape closes; outside click dismisses |
|
|
92
94
|
| `ng generate semmet-angular:tooltip <name>` | [Tooltip](https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/) | Shows on hover **and** focus (required for keyboard users); hides on blur/mouseleave/Escape |
|
|
93
95
|
| `ng generate semmet-angular:menu-button <name>` | [Menu Button](https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/) | Up/Down/Home/End/Escape inside the menu; closes on outside click; focus returns to the trigger |
|
|
94
96
|
| `ng generate semmet-angular:button <name>` | Button | Native `<button>` with variant, size, disabled, busy, and optional pressed state |
|
|
97
|
+
| `ng generate semmet-angular:button-group <name>` | Button Group | Native grouped buttons with `role="group"` and optional `aria-pressed` toggle state |
|
|
98
|
+
| `ng generate semmet-angular:close-button <name>` | Close Button | Native icon button with required accessible label |
|
|
95
99
|
|
|
96
100
|
**Navigation & structure**
|
|
97
101
|
|
|
@@ -100,12 +104,14 @@ By default, projected panel/slide content is preserved after its first render. S
|
|
|
100
104
|
| `ng generate semmet-angular:tabs <name>` | [Tabs](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/) | Automatic activation; Left/Right/Home/End with roving `tabindex` |
|
|
101
105
|
| `ng generate semmet-angular:breadcrumb <name>` | [Breadcrumb](https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/) | Static — plain links + `aria-current="page"` |
|
|
102
106
|
| `ng generate semmet-angular:navigation-menu <name>` | [Navigation Menu (Disclosure)](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-navigation/) | Click/Enter/Space toggles the submenu — deliberately avoids `role="menu"`, reserved for app-style menus |
|
|
107
|
+
| `ng generate semmet-angular:navbar <name>` | Navbar | Semantic `nav`/list/link structure with responsive disclosure toggle and `aria-current` |
|
|
103
108
|
| `ng generate semmet-angular:pagination <name>` | Pagination | Working page state; Previous/Next disable at the boundaries |
|
|
104
109
|
| `ng generate semmet-angular:skip-link <name>` | [Skip Link](https://www.w3.org/WAI/WCAG21/Techniques/general/G1) | Hidden until focused, per the standard skip-link pattern |
|
|
105
110
|
| `ng generate semmet-angular:landmarks <name>` | [Landmarks (Page Skeleton)](https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/) | Content-projected layout wrapper (`header`/`nav`/`main`/`aside`/`footer` slots) |
|
|
106
111
|
| `ng generate semmet-angular:tree-view <name>` | [Tree View](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/) | Up/Down/Left/Right/Home/End/Enter with roving `tabindex` across visible nodes |
|
|
107
112
|
| `ng generate semmet-angular:toolbar <name>` | [Toolbar](https://www.w3.org/WAI/ARIA/apg/patterns/toolbar/) | Left/Right/Home/End with roving `tabindex` |
|
|
108
113
|
| `ng generate semmet-angular:card <name>` | Card | Static — semantic `<article>` + heading structure |
|
|
114
|
+
| `ng generate semmet-angular:list-group <name>` | List Group | Semantic list structure with action items, active state, optional badges, and disabled states |
|
|
109
115
|
| `ng generate semmet-angular:table <name>` | [Table](https://www.w3.org/WAI/ARIA/apg/patterns/table/) | Static — caption + scoped headers |
|
|
110
116
|
|
|
111
117
|
**Forms & inputs**
|
|
@@ -114,6 +120,8 @@ By default, projected panel/slide content is preserved after its first render. S
|
|
|
114
120
|
|---|---|---|
|
|
115
121
|
| `ng generate semmet-angular:form <name>` | Form | Native `<form>` with labeled controls, inline validation, and status messaging |
|
|
116
122
|
| `ng generate semmet-angular:input <name>` | Input | Native `<input>` with label, hint, validation message wiring, and signal-backed value |
|
|
123
|
+
| `ng generate semmet-angular:select <name>` | Select | Native `<select>` with label, hint, validation message wiring, and signal-backed value |
|
|
124
|
+
| `ng generate semmet-angular:input-group <name>` | Input Group | Native `<input>` with prefix/suffix addons announced via `aria-describedby` and optional action button |
|
|
117
125
|
| `ng generate semmet-angular:textarea <name>` | Textarea | Native `<textarea>` with label, hint, counter, validation message wiring, and signal-backed value |
|
|
118
126
|
| `ng generate semmet-angular:checkbox <name>` | [Checkbox](https://www.w3.org/WAI/ARIA/apg/patterns/checkbox/) | Click/Enter/Space toggle |
|
|
119
127
|
| `ng generate semmet-angular:radio-group <name>` | [Radio Group](https://www.w3.org/WAI/ARIA/apg/patterns/radio/) | Up/Down/Left/Right/Home/End move focus and selection together |
|
|
@@ -132,6 +140,8 @@ By default, projected panel/slide content is preserved after its first render. S
|
|
|
132
140
|
| `ng generate semmet-angular:alert <name>` | [Alert](https://www.w3.org/WAI/ARIA/apg/patterns/alert/) | Dismissible assertive live region |
|
|
133
141
|
| `ng generate semmet-angular:toast <name>` | Toast / Notification | Auto-dismisses after 5s (cleaned up via `DestroyRef`); dismissible early |
|
|
134
142
|
| `ng generate semmet-angular:skeleton <name>` | Skeleton Loading | Composable loading placeholder for text, media, circles, cards, and custom blocks |
|
|
143
|
+
| `ng generate semmet-angular:badge <name>` | Badge | Compact status label/counter with decorative mode and optional accessible label |
|
|
144
|
+
| `ng generate semmet-angular:spinner <name>` | Spinner | Loading status with `role="status"`, visually hidden label by default, and reduced-motion support |
|
|
135
145
|
| `ng generate semmet-angular:carousel <name>` | [Carousel](https://www.w3.org/WAI/ARIA/apg/patterns/carousel/) | Previous/Next controls; `aria-live="polite"` slide region |
|
|
136
146
|
|
|
137
147
|
## What every generated component looks like
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "semmet-angular",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Angular schematics that generate real, ARIA-conformant standalone components (Accordion, Tabs, Dialog, Disclosure, Tooltip...) with signals-based state and keyboard interaction built in.",
|
|
5
5
|
"publisher": "danilodevsilva",
|
|
6
6
|
"license": "MIT",
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
--semmet-color-primary: #18181b;
|
|
3
|
+
--semmet-color-on-primary: #ffffff;
|
|
4
|
+
--semmet-color-neutral: #f4f4f5;
|
|
5
|
+
--semmet-color-on-neutral: #27272a;
|
|
6
|
+
--semmet-color-success: #067647;
|
|
7
|
+
--semmet-color-on-success: #ffffff;
|
|
8
|
+
--semmet-color-warning: #b54708;
|
|
9
|
+
--semmet-color-on-warning: #ffffff;
|
|
10
|
+
--semmet-color-danger: #b42318;
|
|
11
|
+
--semmet-color-on-danger: #ffffff;
|
|
12
|
+
--semmet-color-info: #175cd3;
|
|
13
|
+
--semmet-color-on-info: #ffffff;
|
|
14
|
+
--semmet-radius-full: 999px;
|
|
15
|
+
--semmet-radius-sm: 6px;
|
|
16
|
+
|
|
17
|
+
display: inline-block;
|
|
18
|
+
font-family: Roboto, system-ui, -apple-system, 'Segoe UI', sans-serif;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.semmet-badge {
|
|
22
|
+
display: inline-flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
justify-content: center;
|
|
25
|
+
gap: 0.375rem;
|
|
26
|
+
border-radius: var(--semmet-radius-sm);
|
|
27
|
+
font-weight: 700;
|
|
28
|
+
line-height: 1;
|
|
29
|
+
white-space: nowrap;
|
|
30
|
+
vertical-align: baseline;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.semmet-badge--sm {
|
|
34
|
+
min-height: 1.25rem;
|
|
35
|
+
padding: 0.25rem 0.375rem;
|
|
36
|
+
font-size: 0.6875rem;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.semmet-badge--md {
|
|
40
|
+
min-height: 1.5rem;
|
|
41
|
+
padding: 0.3125rem 0.5rem;
|
|
42
|
+
font-size: 0.75rem;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.semmet-badge--lg {
|
|
46
|
+
min-height: 1.75rem;
|
|
47
|
+
padding: 0.375rem 0.625rem;
|
|
48
|
+
font-size: 0.8125rem;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.semmet-badge--pill {
|
|
52
|
+
border-radius: var(--semmet-radius-full);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.semmet-badge--dot {
|
|
56
|
+
min-width: auto;
|
|
57
|
+
padding-inline-start: 0.375rem;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.semmet-badge--dot:has(.semmet-badge__dot):not(:has(.semmet-badge__label)) {
|
|
61
|
+
width: 0.75rem;
|
|
62
|
+
min-height: 0.75rem;
|
|
63
|
+
padding: 0;
|
|
64
|
+
border-radius: var(--semmet-radius-full);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.semmet-badge__dot {
|
|
68
|
+
width: 0.5em;
|
|
69
|
+
height: 0.5em;
|
|
70
|
+
border-radius: var(--semmet-radius-full);
|
|
71
|
+
background: currentColor;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.semmet-badge--neutral {
|
|
75
|
+
color: var(--semmet-color-on-neutral);
|
|
76
|
+
background: var(--semmet-color-neutral);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.semmet-badge--primary {
|
|
80
|
+
color: var(--semmet-color-on-primary);
|
|
81
|
+
background: var(--semmet-color-primary);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.semmet-badge--success {
|
|
85
|
+
color: var(--semmet-color-on-success);
|
|
86
|
+
background: var(--semmet-color-success);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.semmet-badge--warning {
|
|
90
|
+
color: var(--semmet-color-on-warning);
|
|
91
|
+
background: var(--semmet-color-warning);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.semmet-badge--danger {
|
|
95
|
+
color: var(--semmet-color-on-danger);
|
|
96
|
+
background: var(--semmet-color-danger);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.semmet-badge--info {
|
|
100
|
+
color: var(--semmet-color-on-info);
|
|
101
|
+
background: var(--semmet-color-info);
|
|
102
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!-- Semmet Angular: Badge - compact text metadata, status labels, or counters. -->
|
|
2
|
+
<span
|
|
3
|
+
[id]="instanceId"
|
|
4
|
+
[class]="badgeClasses()"
|
|
5
|
+
[attr.aria-hidden]="ariaHidden()"
|
|
6
|
+
[attr.aria-label]="ariaLabel()"
|
|
7
|
+
>
|
|
8
|
+
@if (dot()) {
|
|
9
|
+
<span class="semmet-badge__dot" aria-hidden="true"></span>
|
|
10
|
+
}
|
|
11
|
+
@if (!dot() || label()) {
|
|
12
|
+
<span class="semmet-badge__label">{{ label() }}</span>
|
|
13
|
+
}
|
|
14
|
+
</span>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Component, booleanAttribute, computed, input } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
type <%= classify(name) %>Variant = 'neutral' | 'primary' | 'success' | 'warning' | 'danger' | 'info';
|
|
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('Badge');
|
|
18
|
+
readonly variant = input<<%= classify(name) %>Variant>('neutral');
|
|
19
|
+
readonly size = input<<%= classify(name) %>Size>('md');
|
|
20
|
+
readonly pill = input(false, { transform: booleanAttribute });
|
|
21
|
+
readonly dot = input(false, { transform: booleanAttribute });
|
|
22
|
+
readonly decorative = input(false, { transform: booleanAttribute });
|
|
23
|
+
readonly accessibleLabel = input('');
|
|
24
|
+
|
|
25
|
+
protected readonly badgeClasses = computed(() => [
|
|
26
|
+
'semmet-badge',
|
|
27
|
+
`semmet-badge--${this.variant()}`,
|
|
28
|
+
`semmet-badge--${this.size()}`,
|
|
29
|
+
this.pill() ? 'semmet-badge--pill' : '',
|
|
30
|
+
this.dot() ? 'semmet-badge--dot' : '',
|
|
31
|
+
].filter(Boolean).join(' '));
|
|
32
|
+
|
|
33
|
+
protected readonly ariaHidden = computed(() => this.decorative() ? 'true' : null);
|
|
34
|
+
|
|
35
|
+
protected readonly ariaLabel = computed(() => {
|
|
36
|
+
if (this.decorative()) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const label = this.accessibleLabel().trim();
|
|
41
|
+
return label.length > 0 ? label : null;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const badge: (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.badge = void 0;
|
|
4
|
+
const generate_component_1 = require("../utils/generate-component");
|
|
5
|
+
exports.badge = (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,KAAK,GAAG,IAAA,iDAA4B,GAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ComponentSchematicOptions as Schema } from '../utils/schema-types';
|
|
@@ -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": "SemmetAngularBadge",
|
|
4
|
+
"title": "Semmet Angular Badge Options Schema",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"description": "Generates a standalone Angular Badge component for accessible status labels, counters, and compact metadata.",
|
|
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 badge component?"
|
|
14
|
+
},
|
|
15
|
+
"path": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"format": "path",
|
|
18
|
+
"$default": { "$source": "workingDirectory" },
|
|
19
|
+
"description": "The path where the component files should be created, relative to the workspace root. Defaults to the project's source root.",
|
|
20
|
+
"visible": false
|
|
21
|
+
},
|
|
22
|
+
"project": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"description": "The name of the project to add the component to.",
|
|
25
|
+
"$default": { "$source": "projectName" }
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"required": ["name", "project"]
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ComponentSchematicOptions as Schema } from '../utils/schema-types';
|
|
@@ -0,0 +1,93 @@
|
|
|
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-radius-sm: 8px;
|
|
9
|
+
--semmet-motion: 160ms cubic-bezier(0.2, 0, 0, 1);
|
|
10
|
+
|
|
11
|
+
display: inline-block;
|
|
12
|
+
font-family: Roboto, system-ui, -apple-system, 'Segoe UI', sans-serif;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.semmet-button-group {
|
|
16
|
+
display: inline-flex;
|
|
17
|
+
border: 1px solid var(--semmet-color-outline);
|
|
18
|
+
border-radius: var(--semmet-radius-sm);
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
background: var(--semmet-color-surface);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.semmet-button-group--vertical {
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.semmet-button-group__button {
|
|
28
|
+
display: inline-flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
min-width: 2.75rem;
|
|
32
|
+
border: 0;
|
|
33
|
+
border-inline-end: 1px solid var(--semmet-color-outline);
|
|
34
|
+
color: var(--semmet-color-on-surface);
|
|
35
|
+
background: var(--semmet-color-surface);
|
|
36
|
+
font: inherit;
|
|
37
|
+
font-weight: 650;
|
|
38
|
+
line-height: 1.2;
|
|
39
|
+
white-space: nowrap;
|
|
40
|
+
cursor: pointer;
|
|
41
|
+
transition:
|
|
42
|
+
background-color var(--semmet-motion),
|
|
43
|
+
color var(--semmet-motion),
|
|
44
|
+
opacity var(--semmet-motion);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.semmet-button-group--vertical .semmet-button-group__button {
|
|
48
|
+
border-inline-end: 0;
|
|
49
|
+
border-block-end: 1px solid var(--semmet-color-outline);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.semmet-button-group__button:last-child {
|
|
53
|
+
border-inline-end: 0;
|
|
54
|
+
border-block-end: 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.semmet-button-group--sm .semmet-button-group__button {
|
|
58
|
+
min-height: 2rem;
|
|
59
|
+
padding: 0.375rem 0.75rem;
|
|
60
|
+
font-size: 0.875rem;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.semmet-button-group--md .semmet-button-group__button {
|
|
64
|
+
min-height: 2.5rem;
|
|
65
|
+
padding: 0.5rem 1rem;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.semmet-button-group--lg .semmet-button-group__button {
|
|
69
|
+
min-height: 3rem;
|
|
70
|
+
padding: 0.75rem 1.25rem;
|
|
71
|
+
font-size: 1rem;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.semmet-button-group__button:hover:not(:disabled) {
|
|
75
|
+
background: var(--semmet-color-surface-muted);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.semmet-button-group__button--pressed {
|
|
79
|
+
color: var(--semmet-color-on-primary);
|
|
80
|
+
background: var(--semmet-color-primary);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.semmet-button-group__button:disabled {
|
|
84
|
+
cursor: not-allowed;
|
|
85
|
+
opacity: 0.52;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.semmet-button-group__button:focus-visible {
|
|
89
|
+
position: relative;
|
|
90
|
+
z-index: 1;
|
|
91
|
+
outline: 2px solid var(--semmet-color-primary);
|
|
92
|
+
outline-offset: -2px;
|
|
93
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!-- Semmet Angular: Button Group - grouped native buttons with optional toggle state. -->
|
|
2
|
+
<div
|
|
3
|
+
[id]="instanceId"
|
|
4
|
+
[class]="groupClasses()"
|
|
5
|
+
role="group"
|
|
6
|
+
[attr.aria-label]="label()"
|
|
7
|
+
>
|
|
8
|
+
@for (item of items(); track item.id) {
|
|
9
|
+
<button
|
|
10
|
+
type="button"
|
|
11
|
+
class="semmet-button-group__button"
|
|
12
|
+
[class.semmet-button-group__button--pressed]="isPressed(item)"
|
|
13
|
+
[disabled]="disabled() || item.disabled"
|
|
14
|
+
[attr.aria-pressed]="ariaPressed(item)"
|
|
15
|
+
(click)="activate(item)"
|
|
16
|
+
>
|
|
17
|
+
{{ item.label }}
|
|
18
|
+
</button>
|
|
19
|
+
}
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Component, booleanAttribute, computed, input, signal } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
type <%= classify(name) %>Orientation = 'horizontal' | 'vertical';
|
|
4
|
+
type <%= classify(name) %>SelectionMode = 'none' | 'single' | 'multiple';
|
|
5
|
+
type <%= classify(name) %>Size = 'sm' | 'md' | 'lg';
|
|
6
|
+
|
|
7
|
+
interface <%= classify(name) %>Item {
|
|
8
|
+
id: string;
|
|
9
|
+
label: string;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
pressed?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const DEFAULT_ITEMS: readonly <%= classify(name) %>Item[] = [
|
|
15
|
+
{ id: 'day', label: 'Day', pressed: true },
|
|
16
|
+
{ id: 'week', label: 'Week' },
|
|
17
|
+
{ id: 'month', label: 'Month' },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
let nextId = 0;
|
|
21
|
+
|
|
22
|
+
@Component({
|
|
23
|
+
selector: '<%= selector %>',
|
|
24
|
+
imports: [],
|
|
25
|
+
templateUrl: './<%= dasherize(name) %>.html',
|
|
26
|
+
styleUrl: './<%= dasherize(name) %>.css',
|
|
27
|
+
})
|
|
28
|
+
export class <%= classify(name) %> {
|
|
29
|
+
protected readonly instanceId = `<%= dasherize(name) %>-${nextId++}`;
|
|
30
|
+
|
|
31
|
+
readonly label = input('View options');
|
|
32
|
+
readonly orientation = input<<%= classify(name) %>Orientation>('horizontal');
|
|
33
|
+
readonly selectionMode = input<<%= classify(name) %>SelectionMode>('single');
|
|
34
|
+
readonly size = input<<%= classify(name) %>Size>('md');
|
|
35
|
+
readonly disabled = input(false, { transform: booleanAttribute });
|
|
36
|
+
readonly items = input<readonly <%= classify(name) %>Item[]>(DEFAULT_ITEMS);
|
|
37
|
+
|
|
38
|
+
protected readonly selectedIds = signal<ReadonlySet<string>>(
|
|
39
|
+
new Set(DEFAULT_ITEMS.filter((item) => item.pressed).map((item) => item.id)),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
protected readonly groupClasses = computed(() => [
|
|
43
|
+
'semmet-button-group',
|
|
44
|
+
`semmet-button-group--${this.orientation()}`,
|
|
45
|
+
`semmet-button-group--${this.size()}`,
|
|
46
|
+
].join(' '));
|
|
47
|
+
|
|
48
|
+
protected isPressed(item: <%= classify(name) %>Item): boolean | null {
|
|
49
|
+
if (this.selectionMode() === 'none') {
|
|
50
|
+
return item.pressed ?? null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return this.selectedIds().has(item.id);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
protected ariaPressed(item: <%= classify(name) %>Item): string | null {
|
|
57
|
+
const pressed = this.isPressed(item);
|
|
58
|
+
return pressed === null ? null : String(pressed);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
protected activate(item: <%= classify(name) %>Item): void {
|
|
62
|
+
if (this.disabled() || item.disabled) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (this.selectionMode() === 'none') {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (this.selectionMode() === 'single') {
|
|
71
|
+
this.selectedIds.set(new Set([item.id]));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.selectedIds.update((selectedIds) => {
|
|
76
|
+
const next = new Set(selectedIds);
|
|
77
|
+
if (next.has(item.id)) {
|
|
78
|
+
next.delete(item.id);
|
|
79
|
+
} else {
|
|
80
|
+
next.add(item.id);
|
|
81
|
+
}
|
|
82
|
+
return next;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const buttonGroup: (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.buttonGroup = void 0;
|
|
4
|
+
const generate_component_1 = require("../utils/generate-component");
|
|
5
|
+
exports.buttonGroup = (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 @@
|
|
|
1
|
+
export { ComponentSchematicOptions as Schema } from '../utils/schema-types';
|
|
@@ -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": "SemmetAngularButtonGroup",
|
|
4
|
+
"title": "Semmet Angular Button Group Options Schema",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"description": "Generates a standalone Angular Button Group component with grouped native buttons and optional toggle 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 button 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';
|