@m3e/badge 1.0.0-rc.1 → 1.0.0-rc.2

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.
@@ -1,247 +0,0 @@
1
- import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
2
- import { customElement, property } from "lit/decorators.js";
3
-
4
- import { DesignToken, HtmlFor, Role } from "@m3e/core";
5
- import { M3eDirectionality } from "@m3e/core/bidi";
6
- import { AnchorPosition, positionAnchor } from "@m3e/core/anchoring";
7
-
8
- import { BadgeSize } from "./BadgeSize";
9
- import { BadgePosition } from "./BadgePosition";
10
-
11
- /**
12
- * @summary
13
- * A visual indicator used to label content.
14
- *
15
- * @description
16
- * The `m3e-badge` component is a compact visual indicator used to label content. Designed
17
- * according to Material Design 3 guidelines, it can display counts, presence, or semantic
18
- * emphasis, and is attachable to icons, buttons, or other components. Badges support dynamic
19
- * sizing, color, and shape, ensuring clarity and accessibility while maintaining a consistent,
20
- * expressive appearance across surfaces.
21
- *
22
- * @example
23
- * The following example illustrates attaching a `m3e-badge` to another element using the `for` attribute.
24
- * ```html
25
- * <m3e-button id="button">Button</m3e-button>
26
- * <m3e-badge for="button">10</m3e-badge>
27
- * ```
28
- *
29
- * @tag m3e-badge
30
- *
31
- * @slot - Renders the content of the badge.
32
- *
33
- * @attr size - The size of the badge.
34
- *
35
- * @cssprop --m3e-badge-shape - Corner radius of the badge.
36
- * @cssprop --m3e-badge-color - Foreground color of badge content.
37
- * @cssprop --m3e-badge-container-color - Background color of the badge.
38
- * @cssprop --m3e-badge-small-size - Fixed dimensions for small badge. Used for minimal indicators (e.g. dot).
39
- * @cssprop --m3e-badge-medium-size - Height and min-width for medium badge.
40
- * @cssprop --m3e-badge-medium-font-size - Font size for medium badge label.
41
- * @cssprop --m3e-badge-medium-font-weight - Font weight for medium badge label.
42
- * @cssprop --m3e-badge-medium-line-height - Line height for medium badge label.
43
- * @cssprop --m3e-badge-medium-tracking - Letter spacing for medium badge label.
44
- * @cssprop --m3e-badge-large-size - Height and min-width for large badge.
45
- * @cssprop --m3e-badge-large-font-size - Font size for large badge label.
46
- * @cssprop --m3e-badge-large-font-weight - Font weight for large badge label.
47
- * @cssprop --m3e-badge-large-line-height - Line height for large badge label.
48
- * @cssprop --m3e-badge-large-tracking - Letter spacing for large badge label.
49
- */
50
- @customElement("m3e-badge")
51
- export class M3eBadgeElement extends HtmlFor(Role(LitElement, "status")) {
52
- /** The styles of the element. */
53
- static override styles: CSSResultGroup = css`
54
- :host {
55
- display: inline-flex;
56
- align-items: center;
57
- justify-content: center;
58
- text-align: center;
59
- white-space: nowrap;
60
- vertical-align: baseline;
61
- box-sizing: border-box;
62
- user-select: none;
63
- padding: var(--_badge-padding);
64
- border-radius: var(--m3e-badge-shape, ${DesignToken.shape.corner.full});
65
- color: var(--m3e-badge-color, ${DesignToken.color.onError});
66
- background-color: var(--m3e-badge-container-color, ${DesignToken.color.error});
67
- }
68
- :host([for]) {
69
- position: absolute;
70
- z-index: 1;
71
- }
72
- :host([for]:not([dir])) {
73
- visibility: hidden;
74
- }
75
- :host([for][position^="above"]) {
76
- margin-block-start: var(--_badge-offset, 0px);
77
- }
78
- :host([for][position^="below"]) {
79
- margin-block-start: calc(0px - var(--_badge-offset, 0px));
80
- }
81
- :host([for][position$="before"][dir="ltr"]),
82
- :host([for][position$="after"][dir="rtl"]) {
83
- margin-left: var(--_badge-offset, 0px);
84
- }
85
- :host([for][position$="before"][dir="rtl"]),
86
- :host([for][position$="after"][dir="ltr"]) {
87
- margin-left: calc(0px - var(--_badge-offset, 0px));
88
- }
89
- :host([size="small"]) {
90
- height: var(--m3e-badge-small-size, 0.375rem);
91
- max-height: var(--m3e-badge-small-size, 0.375rem);
92
- width: var(--m3e-badge-small-size, 0.375rem);
93
- min-width: var(--m3e-badge-small-size, 0.375rem);
94
- font-size: 0;
95
- --_badge-offset: var(--m3e-badge-small-offset, 0.375rem);
96
- }
97
- :host([size="medium"]) {
98
- height: var(--m3e-badge-medium-size, 1.375rem);
99
- min-width: var(--m3e-badge-medium-size, 1.375rem);
100
- font-size: var(--m3e-badge-medium-font-size, ${DesignToken.typescale.standard.label.small.fontSize});
101
- font-weight: var(--m3e-badge-medium-font-weight, ${DesignToken.typescale.standard.label.small.fontWeight});
102
- line-height: var(--m3e-badge-medium-line-height, ${DesignToken.typescale.standard.label.small.lineHeight});
103
- letter-spacing: var(--m3e-badge-medium-tracking, ${DesignToken.typescale.standard.label.small.tracking});
104
- --_badge-offset: var(--m3e-badge-small-offset, 0.75rem);
105
- }
106
- :host([size="large"]) {
107
- height: var(--m3e-badge-large-size, 1.75rem);
108
- min-width: var(--m3e-badge-large-size, 1.75rem);
109
- font-size: var(--m3e-badge-large-font-size, ${DesignToken.typescale.standard.label.large.fontSize});
110
- font-weight: var(--m3e-badge-large-font-weight, ${DesignToken.typescale.standard.label.large.fontWeight});
111
- line-height: var(--m3e-badge-large-line-height, ${DesignToken.typescale.standard.label.large.lineHeight});
112
- letter-spacing: var(--m3e-badge-large-tracking, ${DesignToken.typescale.standard.label.large.tracking});
113
- --_badge-offset: var(--m3e-badge-small-offset, 1rem);
114
- }
115
- @media (forced-colors: active) {
116
- :host {
117
- background-color: ButtonFace;
118
- color: ButtonText;
119
- outline: 1px solid ButtonText;
120
- }
121
- }
122
- `;
123
-
124
- /** @private */ #directionalitySubscription?: () => void;
125
- /** @private */ #anchorCleanup?: () => void;
126
-
127
- /**
128
- * The size of the badge.
129
- * @default "medium"
130
- */
131
- @property({ reflect: true }) size: BadgeSize = "medium";
132
-
133
- /**
134
- * The position of the badge, when attached to another element.
135
- * @default "above-after"
136
- */
137
- @property({ reflect: true }) position: BadgePosition = "above-after";
138
-
139
- /** @inheritdoc */
140
- override detach(): void {
141
- super.detach();
142
- this.#detach();
143
- }
144
-
145
- /** @inheritdoc */
146
- override connectedCallback(): void {
147
- super.connectedCallback();
148
- this.#directionalitySubscription = M3eDirectionality.observe(() => this.#attach());
149
- }
150
-
151
- /** @inheritdoc */
152
- override disconnectedCallback(): void {
153
- super.disconnectedCallback();
154
- this.#directionalitySubscription?.();
155
- }
156
-
157
- /** @inheritdoc */
158
- protected override update(changedProperties: PropertyValues<this>): void {
159
- super.update(changedProperties);
160
-
161
- if (changedProperties.has("position") || changedProperties.has("size") || changedProperties.has("htmlFor")) {
162
- this.#attach();
163
- }
164
- }
165
-
166
- /** @inheritdoc */
167
- protected override render(): unknown {
168
- return html`<slot @slotchange="${this.#handleSlotChange}"> <span aria-hidden="true">&nbsp;</span></slot>`;
169
- }
170
-
171
- /** @internal */
172
- #handleSlotChange() {
173
- if (!this.isConnected) return;
174
- this.style.setProperty(
175
- "--_badge-padding",
176
- this.textContent && this.textContent.length > 2
177
- ? `0 ${this.size === "medium" ? "0.25rem" : this.size === "large" ? "0.5rem" : "0"}`
178
- : ""
179
- );
180
- }
181
-
182
- /** @private */
183
- #detach(): void {
184
- this.#anchorCleanup?.();
185
- this.#anchorCleanup = undefined;
186
- this.dir = "";
187
- }
188
-
189
- /** @private */
190
- async #attach(): Promise<void> {
191
- this.#detach();
192
-
193
- if (!this.control) return;
194
-
195
- let position: AnchorPosition = "top-end";
196
- switch (this.position) {
197
- case "above":
198
- position = "top";
199
- break;
200
- case "above-before":
201
- position = "top-start";
202
- break;
203
- case "after":
204
- position = "right";
205
- break;
206
- case "before":
207
- position = "left";
208
- break;
209
- case "below":
210
- position = "bottom";
211
- break;
212
- case "below-after":
213
- position = "bottom-end";
214
- break;
215
- case "below-before":
216
- position = "bottom-start";
217
- break;
218
- }
219
-
220
- this.#anchorCleanup = await positionAnchor(this, this.control, { position }, (x, y) => {
221
- this.dir = M3eDirectionality.current;
222
- if (this.position.includes("before")) {
223
- if (this.dir == "rtl") {
224
- x += this.clientWidth;
225
- } else {
226
- x -= this.clientWidth;
227
- }
228
- }
229
- if (this.position.includes("after")) {
230
- if (this.dir == "rtl") {
231
- x -= this.clientWidth;
232
- } else {
233
- x += this.clientWidth;
234
- }
235
- }
236
-
237
- this.style.left = `${x}px`;
238
- this.style.top = `${y}px`;
239
- });
240
- }
241
- }
242
-
243
- declare global {
244
- interface HTMLElementTagNameMap {
245
- "m3e-badge": M3eBadgeElement;
246
- }
247
- }
@@ -1,10 +0,0 @@
1
- /** Specifies the possible positions of a badge, when attached to another element. */
2
- export type BadgePosition =
3
- | "above-after"
4
- | "above-before"
5
- | "below-before"
6
- | "below-after"
7
- | "before"
8
- | "after"
9
- | "above"
10
- | "below";
package/src/BadgeSize.ts DELETED
@@ -1,2 +0,0 @@
1
- /** Specifies the possible size variants for a badge. */
2
- export type BadgeSize = "small" | "medium" | "large";
package/src/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from "./BadgeElement";
2
- export * from "./BadgeSize";
package/tsconfig.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "rootDir": "./src",
5
- "outDir": "./dist/src"
6
- },
7
- "include": ["src/**/*.ts", "**/*.mjs", "**/*.js"],
8
- "exclude": []
9
- }