@m3e/nav-menu 1.0.0-rc.1 → 1.0.0-rc.3

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,560 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */
2
- import { css, CSSResultGroup, html, LitElement, PropertyValues, unsafeCSS } from "lit";
3
- import { customElement, property, query, state } from "lit/decorators.js";
4
- import { ifDefined } from "lit/directives/if-defined.js";
5
-
6
- import {
7
- AttachInternals,
8
- DesignToken,
9
- Disabled,
10
- EventAttribute,
11
- hasAssignedNodes,
12
- M3eFocusRingElement,
13
- M3eRippleElement,
14
- M3eStateLayerElement,
15
- Role,
16
- Selected,
17
- } from "@m3e/core";
18
-
19
- import { selectionManager } from "@m3e/core/a11y";
20
-
21
- import type { M3eNavMenuElement } from "./NavMenuElement";
22
-
23
- /**
24
- * @summary
25
- * An expandable item in a navigation menu.
26
- *
27
- * @description
28
- * The `m3e-nav-menu-item` component represents an expandable, selectable item within a navigation menu.
29
- * It supports nested child items, selection, disabled and indeterminate states, and emits events for
30
- * open/close transitions. The component is highly customizable via slots and CSS custom properties, and
31
- * is designed for accessible, keyboard-navigable menu structures.
32
- *
33
- * @example
34
- * The following example illustrates a multilevel navigation menu.
35
- * ```html
36
- * <m3e-nav-menu>
37
- * <m3e-nav-menu-item open>
38
- * <m3e-icon slot="icon" name="rocket_launch"></m3e-icon>
39
- * <span slot="label">Getting Started</span>
40
- * <m3e-nav-menu-item>
41
- * <m3e-icon slot="icon" name="widgets"></m3e-icon>
42
- * <span slot="label">Overview</span>
43
- * </m3e-nav-menu-item>
44
- * <m3e-nav-menu-item>
45
- * <m3e-icon slot="icon" name="package_2"></m3e-icon>
46
- * <span slot="label">Installation</span>
47
- * </m3e-nav-menu-item>
48
- * </m3e-nav-menu-item>
49
- * <m3e-nav-menu-item>
50
- * <span slot="label">Actions</span>
51
- * <m3e-nav-menu-item><span slot="label">Button</span></m3e-nav-menu-item>
52
- * <m3e-nav-menu-item><span slot="label">Icon</span></m3e-nav-menu-item>
53
- * <m3e-nav-menu-item><span slot="label">Icon Button</span></m3e-nav-menu-item>
54
- * </m3e-nav-menu-item>
55
- * </m3e-nav-menu>
56
- * ```
57
- *
58
- * @tag m3e-nav-menu-item
59
- *
60
- * @slot - Renders the nested child items.
61
- * @slot label - Renders the label of the item.
62
- * @slot icon - Renders the icon of the item.
63
- * @slot selected-icon - Renders the icon of the item when selected.
64
- * @slot toggle-icon - Renders the toggle icon.
65
- *
66
- * @attr disabled - Whether the element is disabled.
67
- * @attr indeterminate - Whether the element's selected / checked state is indeterminate.
68
- * @attr open - Whether the item is expanded.
69
- * @attr selected - Whether the item is selected.
70
- *
71
- * @fires opening - Emitted when the item begins to open.
72
- * @fires opened - Emitted when the item has opened.
73
- * @fires closing - Emitted when the item begins to close.
74
- * @fires closed - Emitted when the item has closed.
75
- *
76
- * @cssprop --m3e-nav-menu-item-font-size - Font size for the item label.
77
- * @cssprop --m3e-nav-menu-item-font-weight - Font weight for the item label.
78
- * @cssprop --m3e-nav-menu-item-line-height - Line height for the item label.
79
- * @cssprop --m3e-nav-menu-item-tracking - Letter spacing for the item label.
80
- * @cssprop --m3e-nav-menu-item-padding - Inline padding for the item.
81
- * @cssprop --m3e-nav-menu-item-height - Height of the item.
82
- * @cssprop --m3e-nav-menu-item-spacing - Spacing between icon and label.
83
- * @cssprop --m3e-nav-menu-item-shape - Border radius of the item and focus ring.
84
- * @cssprop --m3e-nav-menu-item-icon-size - Size of the icon.
85
- * @cssprop --m3e-nav-menu-item-inset - Indentation for nested items.
86
- * @cssprop --m3e-nav-menu-item-label-color - Text color for the item label.
87
- * @cssprop --m3e-nav-menu-item-selected-label-color - Text color for selected item label.
88
- * @cssprop --m3e-nav-menu-item-selected-container-color - Background color for selected item.
89
- * @cssprop --m3e-nav-menu-item-selected-container-focus-color - Focus color for selected item container.
90
- * @cssprop --m3e-nav-menu-item-selected-container-hover-color - Hover color for selected item container.
91
- * @cssprop --m3e-nav-menu-item-selected-ripple-color - Ripple color for selected item.
92
- * @cssprop --m3e-nav-menu-item-unselected-container-focus-color - Focus color for unselected item container.
93
- * @cssprop --m3e-nav-menu-item-unselected-container-hover-color - Hover color for unselected item container.
94
- * @cssprop --m3e-nav-menu-item-unselected-ripple-color - Ripple color for unselected item.
95
- * @cssprop --m3e-nav-menu-item-open-container-color - Background color for open item with children.
96
- * @cssprop --m3e-nav-menu-item-open-container-focus-color - Focus color for open item container.
97
- * @cssprop --m3e-nav-menu-item-open-container-hover-color - Hover color for open item container.
98
- * @cssprop --m3e-nav-menu-item-open-ripple-color - Ripple color for open item.
99
- * @cssprop --m3e-nav-menu-item-disabled-color - Text color for disabled item.
100
- * @cssprop --m3e-nav-menu-item-disabled-color-opacity - Opacity for disabled item text color.
101
- * @cssprop --m3e-nav-menu-item-badge-font-size - Font size for badge slot.
102
- * @cssprop --m3e-nav-menu-item-badge-font-weight - Font weight for badge slot.
103
- * @cssprop --m3e-nav-menu-item-badge-line-height - Line height for badge slot.
104
- * @cssprop --m3e-nav-menu-badge-item-tracking - Letter spacing for badge slot.
105
- * @cssprop --m3e-nav-menu-divider-margin - Margin for divider elements.
106
- * @cssprop --m3e-nav-menu-item-vertical-inset - Vertical margin for first/last child items.
107
- */
108
- @customElement("m3e-nav-menu-item")
109
- export class M3eNavMenuItemElement extends Selected(
110
- Disabled(
111
- EventAttribute(AttachInternals(Role(LitElement, "treeitem"), true), "opening", "opened", "closing", "closed")
112
- )
113
- ) {
114
- /** The styles of the element. */
115
- static override styles: CSSResultGroup = css`
116
- :host {
117
- display: block;
118
- flex: none;
119
- outline: none;
120
- position: relative;
121
- font-size: var(--m3e-nav-menu-item-font-size, ${DesignToken.typescale.standard.label.large.fontSize});
122
- font-weight: var(--m3e-nav-menu-item-font-weight, ${DesignToken.typescale.standard.label.large.fontWeight});
123
- line-height: var(--m3e-nav-menu-item-line-height, ${DesignToken.typescale.standard.label.large.lineHeight});
124
- letter-spacing: var(--m3e-nav-menu-item-tracking, ${DesignToken.typescale.standard.label.large.tracking});
125
- }
126
- .base {
127
- display: flex;
128
- align-items: center;
129
- position: relative;
130
- padding-inline: var(--m3e-nav-menu-item-padding, 1.5rem);
131
- height: calc(var(--m3e-nav-menu-item-height, 3.5rem) + ${DesignToken.density.calc(-3)});
132
- column-gap: var(--m3e-nav-menu-item-spacing, 0.75rem);
133
- transition: ${unsafeCSS(
134
- `color ${DesignToken.motion.duration.short4} ${DesignToken.motion.easing.standard},
135
- background-color ${DesignToken.motion.duration.short4} ${DesignToken.motion.easing.standard}`
136
- )};
137
- }
138
- .base,
139
- .focus-ring {
140
- border-radius: var(--m3e-nav-menu-item-shape, ${DesignToken.shape.corner.full});
141
- }
142
- .label {
143
- flex: 1 1 auto;
144
- display: flex;
145
- align-items: center;
146
- position: relative;
147
- overflow: hidden;
148
- vertical-align: middle;
149
- }
150
- .icon,
151
- .toggle {
152
- flex: none;
153
- display: flex;
154
- align-items: center;
155
- justify-content: center;
156
- position: relative;
157
- vertical-align: middle;
158
- }
159
- ::slotted([slot="badge"]) {
160
- flex: none;
161
- position: relative;
162
- font-size: var(--m3e-nav-menu-item-badge-font-size, ${DesignToken.typescale.standard.label.large.fontSize});
163
- font-weight: var(--m3e-nav-menu-item-badge-font-weight, ${DesignToken.typescale.standard.label.large.fontWeight});
164
- line-height: var(--m3e-nav-menu-item-badge-line-height, ${DesignToken.typescale.standard.label.large.lineHeight});
165
- letter-spacing: var(--m3e-nav-menu-badge-item-tracking, ${DesignToken.typescale.standard.label.large.tracking});
166
- }
167
- .toggle {
168
- transition: ${unsafeCSS(`transform var(--m3e-collapsible-animation-duration, ${DesignToken.motion.duration.medium1})
169
- ${DesignToken.motion.easing.standard}`)};
170
- }
171
- :host(:not(.-with-icon)) .icon {
172
- display: none;
173
- }
174
- .icon {
175
- margin-inline-start: -0.5rem;
176
- }
177
- .toggle {
178
- margin-inline-end: -0.5rem;
179
- }
180
- .group {
181
- padding-inline-start: var(--m3e-nav-menu-item-inset, 1rem);
182
- }
183
- :host([open]) .toggle {
184
- transform: rotate(180deg);
185
- }
186
- :host(:not(.-has-items)) .toggle,
187
- :host(:not(.-has-items)) .group {
188
- display: none;
189
- }
190
- ::slotted([slot="selected-icon"]),
191
- ::slotted([slot="icon"]),
192
- ::slotted([slot="toggle-icon"]),
193
- .toggle-icon {
194
- vertical-align: middle;
195
- width: 1em;
196
- height: 1em;
197
- font-size: var(--m3e-nav-menu-item-icon-size, 1.5rem);
198
- }
199
- :host(:not(:disabled)) .base {
200
- cursor: pointer;
201
- }
202
- :host(:not(:disabled)) .base {
203
- color: var(--m3e-nav-menu-item-label-color, ${DesignToken.color.onSurfaceVariant});
204
- }
205
- :host(:disabled) .base {
206
- color: color-mix(
207
- in srgb,
208
- var(--m3e-nav-menu-item-disabled-color, ${DesignToken.color.onSurface})
209
- var(--m3e-nav-menu-item-disabled-color-opacity, 38%),
210
- transparent
211
- );
212
- }
213
- :host([selected]:not(.-has-items):not(:disabled)) .base {
214
- color: var(--m3e-nav-menu-item-selected-label-color, ${DesignToken.color.onSecondaryContainer});
215
- background-color: var(--m3e-nav-menu-item-selected-container-color, ${DesignToken.color.secondaryContainer});
216
- --m3e-state-layer-focus-color: var(
217
- --m3e-nav-menu-item-selected-container-focus-color,
218
- ${DesignToken.color.onSecondaryContainer}
219
- );
220
- --m3e-state-layer-hover-color: var(
221
- --m3e-nav-menu-item-selected-container-hover-color,
222
- ${DesignToken.color.onSecondaryContainer}
223
- );
224
- --m3e-ripple-color: var(--m3e-nav-menu-item-selected-ripple-color, ${DesignToken.color.onSecondaryContainer});
225
- }
226
- :host(:not([selected]):not(.-has-items):not(:disabled)) .base {
227
- --m3e-state-layer-focus-color: var(
228
- --m3e-nav-menu-item-unselected-container-focus-color,
229
- ${DesignToken.color.onSurface}
230
- );
231
- --m3e-state-layer-hover-color: var(
232
- --m3e-nav-menu-item-unselected-container-hover-color,
233
- ${DesignToken.color.onSurface}
234
- );
235
- --m3e-ripple-color: var(--m3e-nav-menu-item-unselected-ripple-color, ${DesignToken.color.onSurface});
236
- }
237
- .state-layer {
238
- margin-inline: auto;
239
- }
240
- :host([selected].-has-items:not(:disabled)) .base {
241
- background-color: var(--m3e-nav-menu-item-open-container-color, ${DesignToken.color.surfaceContainerHighest});
242
- --m3e-state-layer-focus-color: var(
243
- --m3e-nav-menu-item-open-container-focus-color,
244
- ${DesignToken.color.onSurface}
245
- );
246
- --m3e-state-layer-hover-color: var(
247
- --m3e-nav-menu-item-open-container-hover-color,
248
- ${DesignToken.color.onSurface}
249
- );
250
- --m3e-ripple-color: var(--m3e-nav-menu-item-open-ripple-color, ${DesignToken.color.onSurface});
251
- }
252
- ::slotted(a[slot="label"]) {
253
- all: unset;
254
- }
255
- ::slotted(m3e-divider) {
256
- margin-block: var(--m3e-nav-menu-divider-margin, 0.25rem);
257
- }
258
- ::slotted(m3e-nav-menu-item:first-of-type) {
259
- margin-block-start: var(--m3e-nav-menu-item-vertical-inset, 0.25rem);
260
- }
261
- ::slotted(m3e-nav-menu-item:last-of-type) {
262
- margin-block-end: var(--m3e-nav-menu-item-vertical-inset, 0.25rem);
263
- }
264
- @media (prefers-reduced-motion) {
265
- .base,
266
- .toggle,
267
- .state-layer {
268
- transition: none !important;
269
- }
270
- }
271
- @media (forced-colors: active) {
272
- .base,
273
- .state-layer {
274
- transition: none !important;
275
- }
276
-
277
- :host(:disabled) .base {
278
- color: GrayText;
279
- }
280
- :host(:not([selected]):not(:disabled)) .base {
281
- color: ButtonText;
282
- }
283
- :host([selected]:not(.-has-items):not(:disabled)) .base {
284
- forced-color-adjust: none;
285
- color: ButtonFace;
286
- background-color: ButtonText;
287
- }
288
- :host([selected].-has-items:not(:disabled)) .base {
289
- background-color: unset;
290
- color: ButtonText;
291
- }
292
- }
293
- `;
294
-
295
- /** @internal */ @query(".state-layer") readonly stateLayer?: M3eStateLayerElement;
296
- /** @internal */ @query(".focus-ring") readonly focusRing?: M3eFocusRingElement;
297
- /** @internal */ @query(".ripple") readonly ripple?: M3eRippleElement;
298
- /** @private */ @query(".base") private readonly _base?: HTMLElement;
299
-
300
- /** @private */ @state() private _hasChildItems = false;
301
-
302
- /** @private */ #items: M3eNavMenuItemElement[] = [];
303
- /** @private */ #menu: M3eNavMenuElement | null = null;
304
- /** @private */ #path = new Array<M3eNavMenuItemElement>();
305
- /** @private */ #link: HTMLAnchorElement | null = null;
306
-
307
- /**
308
- * Whether the item is expanded.
309
- * @default false
310
- */
311
- @property({ type: Boolean, reflect: true }) open = false;
312
-
313
- /** A reference to the nested `HTMLAnchorElement`. */
314
- get link(): HTMLAnchorElement | null {
315
- return this.#link;
316
- }
317
-
318
- /** Whether the item is visible. */
319
- get visible(): boolean {
320
- return !this.#path.some((x) => !x.open);
321
- }
322
-
323
- /** The full path of the item, starting with the top-most ancestor, including this item. */
324
- get path(): ReadonlyArray<M3eNavMenuItemElement> {
325
- return [...this.#path, this];
326
- }
327
-
328
- /** Whether the item has child items. */
329
- get hasChildItems(): boolean {
330
- return this._hasChildItems;
331
- }
332
-
333
- /** The parenting item. */
334
- get parentItem(): M3eNavMenuItemElement | null {
335
- return this.#path[this.#path.length - 1] ?? null;
336
- }
337
-
338
- /** The items that immediately descend from this item. */
339
- get childItems(): readonly M3eNavMenuItemElement[] {
340
- return this.#items;
341
- }
342
-
343
- /** The one-based level of the item. */
344
- get level(): number {
345
- return this.#path.length + 1;
346
- }
347
-
348
- /**
349
- * Expands this item, and optionally, all descendants.
350
- * @param {boolean} [descendants=false] Whether to expand all descendants.
351
- */
352
- expand(descendants: boolean = false): void {
353
- if (this.hasChildItems) {
354
- this.open = true;
355
- if (descendants) {
356
- this.childItems.forEach((x) => x.expand(true));
357
- }
358
- }
359
- }
360
-
361
- /**
362
- * Collapses this item, and optionally, all descendants.
363
- * @param {boolean} [descendants=false] Whether to expand all descendants.
364
- */
365
- collapse(descendants: boolean = false): void {
366
- if (this.hasChildItems) {
367
- this.open = false;
368
- if (descendants) {
369
- this.childItems.forEach((x) => x.collapse(true));
370
- }
371
- }
372
- }
373
-
374
- /** Toggles the expanded state of the item. */
375
- toggle(): void {
376
- if (this.hasChildItems) {
377
- this.open = !this.open;
378
- }
379
- }
380
-
381
- /** @inheritdoc */
382
- override connectedCallback(): void {
383
- super.connectedCallback();
384
-
385
- this.#path.length = 0;
386
- for (
387
- let item = this.parentElement?.closest("m3e-nav-menu-item");
388
- item;
389
- item = item.parentElement?.closest("m3e-nav-menu-item")
390
- ) {
391
- this.#path.push(item);
392
- }
393
- this.#path.reverse();
394
-
395
- this.style.setProperty("--_nav-menu-item-level", `${this.level}`);
396
- this.#menu = this.closest("m3e-nav-menu");
397
- }
398
-
399
- /** @inheritdoc */
400
- override disconnectedCallback(): void {
401
- super.disconnectedCallback();
402
- this.#path.length = 0;
403
- }
404
-
405
- /** @inheritdoc */
406
- protected override update(changedProperties: PropertyValues<this>): void {
407
- super.update(changedProperties);
408
-
409
- if (changedProperties.has("selected")) {
410
- this.ariaCurrent = this.hasChildItems ? null : `${this.selected}`;
411
- for (const icon of this.querySelectorAll(":scope > m3e-icon[slot]")) {
412
- icon.toggleAttribute("filled", this.selected);
413
- }
414
-
415
- this.#path.forEach((x) => (x.selected = this.selected));
416
- if (this.selected && !this.hasChildItems) {
417
- this.closest("m3e-nav-menu")?.[selectionManager].notifySelectionChange(this);
418
- }
419
- }
420
- }
421
-
422
- /** @inheritdoc */
423
- protected override firstUpdated(_changedProperties: PropertyValues<this>): void {
424
- super.firstUpdated(_changedProperties);
425
-
426
- const base = this._base;
427
- if (base) {
428
- [this.focusRing, this.stateLayer, this.ripple].forEach((x) => x?.attach(base));
429
- }
430
- }
431
-
432
- /** @inheritdoc */
433
- protected override render(): unknown {
434
- return html`<div class="base" @click="${this.#handleClick}">
435
- <m3e-state-layer class="state-layer" ?disabled="${this.disabled}"></m3e-state-layer>
436
- <m3e-focus-ring class="focus-ring" inward ?disabled="${this.disabled}"></m3e-focus-ring>
437
- <m3e-ripple class="ripple" centered ?disabled="${this.disabled}"></m3e-ripple>
438
- <div class="icon">${this.#renderIcon()}</div>
439
- <div class="label">
440
- <slot name="label" @slotchange="${this.#handleSlotChange}"></slot>
441
- </div>
442
- <slot name="badge"></slot>
443
- <div aria-hidden="true" class="toggle">
444
- <slot name="toggle-icon">
445
- <svg class="toggle-icon" viewBox="0 -960 960 960" fill="currentColor">
446
- <path d="M480-360 280-560h400L480-360Z" />
447
- </svg>
448
- </slot>
449
- </div>
450
- </div>
451
- <m3e-collapsible
452
- class="group"
453
- role="group"
454
- aria-hidden="${ifDefined(this._hasChildItems ? undefined : "true")}"
455
- ?open="${this._hasChildItems && this.open}"
456
- @opening="${this.#handleCollapsibleEvent}"
457
- @opened="${this.#handleCollapsibleEvent}"
458
- @closing="${this.#handleCollapsibleEvent}"
459
- @closed="${this.#handleCollapsibleEvent}"
460
- >
461
- <slot @slotchange="${this.#handleItemSlotChange}"></slot>
462
- </m3e-collapsible>`;
463
- }
464
-
465
- /** @private */
466
- #renderIcon(): unknown {
467
- const icon = html`<slot name="icon" @slotchange="${this.#handleIconSlotChange}"></slot>`;
468
- return this.selected && !this.hasChildItems
469
- ? html`<slot name="selected-icon" @slotchange="${this.#handleIconSlotChange}">${icon}</slot>`
470
- : icon;
471
- }
472
-
473
- /** @private */
474
- #handleIconSlotChange(e: Event) {
475
- this.classList.toggle("-with-icon", hasAssignedNodes(<HTMLSlotElement>e.target));
476
- }
477
-
478
- /** @private */
479
- #handleSlotChange(e: Event): void {
480
- this.#link =
481
- (<HTMLSlotElement>e.target).assignedElements({ flatten: true }).find((x) => x instanceof HTMLAnchorElement) ??
482
- null;
483
-
484
- this.#link?.setAttribute("tabindex", "-1");
485
- }
486
-
487
- /** @private */
488
- #handleItemSlotChange(e: Event): void {
489
- this.#items = (<HTMLSlotElement>e.target)
490
- .assignedElements({ flatten: true })
491
- .filter((x) => x instanceof M3eNavMenuItemElement);
492
-
493
- const hadChildItems = this._hasChildItems;
494
- this._hasChildItems = this.#items.length > 0;
495
- this.classList.toggle("-has-items", this._hasChildItems);
496
-
497
- if (hadChildItems || this._hasChildItems) {
498
- this.selected = this.#items.some((x) => x.selected);
499
- }
500
- }
501
-
502
- /** @private */
503
- #handleClick(): void {
504
- if (this.disabled) return;
505
-
506
- this.#menu?.[selectionManager].setActiveItem(this);
507
- if (!this._hasChildItems) {
508
- this.#menu?.[selectionManager].select(this);
509
- this.#path.forEach((x) => (x.selected = this.selected));
510
- this.#link?.click();
511
- } else {
512
- this.toggle();
513
- }
514
- }
515
-
516
- /** @private */
517
- #handleCollapsibleEvent(e: Event): void {
518
- e.stopPropagation();
519
- this.dispatchEvent(new Event(e.type, { bubbles: true }));
520
- }
521
- }
522
-
523
- interface M3eNavMenuItemElementEventMap extends HTMLElementEventMap {
524
- opening: Event;
525
- opened: Event;
526
- closing: Event;
527
- closed: Event;
528
- }
529
-
530
- export interface M3eNavMenuItemElement {
531
- addEventListener<K extends keyof M3eNavMenuItemElementEventMap>(
532
- type: K,
533
- listener: (this: M3eNavMenuItemElement, ev: M3eNavMenuItemElementEventMap[K]) => void,
534
- options?: boolean | AddEventListenerOptions
535
- ): void;
536
-
537
- addEventListener(
538
- type: string,
539
- listener: EventListenerOrEventListenerObject,
540
- options?: boolean | AddEventListenerOptions
541
- ): void;
542
-
543
- removeEventListener<K extends keyof M3eNavMenuItemElementEventMap>(
544
- type: K,
545
- listener: (this: M3eNavMenuItemElement, ev: M3eNavMenuItemElementEventMap[K]) => void,
546
- options?: boolean | EventListenerOptions
547
- ): void;
548
-
549
- removeEventListener(
550
- type: string,
551
- listener: EventListenerOrEventListenerObject,
552
- options?: boolean | EventListenerOptions
553
- ): void;
554
- }
555
-
556
- declare global {
557
- interface HTMLElementTagNameMap {
558
- "m3e-nav-menu-item": M3eNavMenuItemElement;
559
- }
560
- }
package/src/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from "./NavMenuElement";
2
- export * from "./NavMenuItemElement";
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
- }