@smuikit/angular 0.1.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.
@@ -0,0 +1,216 @@
1
+ import {
2
+ Component,
3
+ Input,
4
+ Output,
5
+ EventEmitter,
6
+ NgZone,
7
+ OnInit,
8
+ OnDestroy,
9
+ OnChanges,
10
+ SimpleChanges,
11
+ ElementRef,
12
+ ViewChild,
13
+ ChangeDetectionStrategy,
14
+ ChangeDetectorRef,
15
+ HostListener,
16
+ } from '@angular/core';
17
+ import { CommonModule } from '@angular/common';
18
+ import {
19
+ createModalMachine,
20
+ connectModal,
21
+ type ModalConnectReturn,
22
+ type ModalState,
23
+ type ModalContext,
24
+ type ModalEvent,
25
+ } from '@smuikit/core';
26
+ import { MachineAdapter } from '../../use-machine.service';
27
+ import { normalizeProps, applyProps } from '../../normalize-props';
28
+
29
+ @Component({
30
+ selector: 'smui-modal',
31
+ standalone: true,
32
+ imports: [CommonModule],
33
+ changeDetection: ChangeDetectionStrategy.OnPush,
34
+ template: `
35
+ <!-- Trigger slot -->
36
+ <div #triggerEl class="smui-modal__trigger" (click)="open()">
37
+ <ng-content select="[smuiModalTrigger]"></ng-content>
38
+ </div>
39
+
40
+ <!-- Overlay + Content (portal-like fixed overlay) -->
41
+ <ng-container *ngIf="api.isOpen">
42
+ <div
43
+ #overlayEl
44
+ class="smui-modal__overlay"
45
+ [attr.data-state]="api.state"
46
+ (click)="onOverlayClick($event)"
47
+ ></div>
48
+ <div
49
+ #contentEl
50
+ class="smui-modal__content"
51
+ [attr.data-state]="api.state"
52
+ role="dialog"
53
+ aria-modal="true"
54
+ [attr.aria-labelledby]="titleId"
55
+ tabindex="-1"
56
+ >
57
+ <div class="smui-modal__header">
58
+ <div [attr.id]="titleId" class="smui-modal__title">
59
+ <ng-content select="[smuiModalTitle]"></ng-content>
60
+ </div>
61
+ <button
62
+ #closeEl
63
+ class="smui-modal__close"
64
+ aria-label="Close"
65
+ (click)="close()"
66
+ type="button"
67
+ >
68
+ &times;
69
+ </button>
70
+ </div>
71
+ <div class="smui-modal__body">
72
+ <ng-content></ng-content>
73
+ </div>
74
+ </div>
75
+ </ng-container>
76
+ `,
77
+ styles: [
78
+ `
79
+ :host {
80
+ display: inline-block;
81
+ }
82
+ .smui-modal__overlay {
83
+ position: fixed;
84
+ inset: 0;
85
+ background: rgba(0, 0, 0, 0.5);
86
+ z-index: 999;
87
+ }
88
+ .smui-modal__content {
89
+ position: fixed;
90
+ top: 50%;
91
+ left: 50%;
92
+ transform: translate(-50%, -50%);
93
+ background: var(--smui-color-surface, #ffffff);
94
+ border-radius: 8px;
95
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
96
+ z-index: 1000;
97
+ min-width: 320px;
98
+ max-width: 90vw;
99
+ max-height: 90vh;
100
+ overflow-y: auto;
101
+ outline: none;
102
+ }
103
+ .smui-modal__header {
104
+ display: flex;
105
+ align-items: center;
106
+ justify-content: space-between;
107
+ padding: 16px 24px;
108
+ border-bottom: 1px solid var(--smui-color-border, #e5e7eb);
109
+ }
110
+ .smui-modal__title {
111
+ font-weight: 600;
112
+ font-size: 1.125rem;
113
+ }
114
+ .smui-modal__close {
115
+ background: none;
116
+ border: none;
117
+ font-size: 1.5rem;
118
+ cursor: pointer;
119
+ padding: 4px 8px;
120
+ line-height: 1;
121
+ border-radius: 4px;
122
+ }
123
+ .smui-modal__close:hover {
124
+ background: var(--smui-color-hover, #f3f4f6);
125
+ }
126
+ .smui-modal__body {
127
+ padding: 24px;
128
+ }
129
+ `,
130
+ ],
131
+ })
132
+ export class ModalComponent implements OnInit, OnDestroy, OnChanges {
133
+ @Input() closeOnOverlayClick = true;
134
+ @Input() closeOnEscape = true;
135
+
136
+ @Output() openChange = new EventEmitter<boolean>();
137
+
138
+ @ViewChild('triggerEl') triggerElRef?: ElementRef<HTMLElement>;
139
+ @ViewChild('contentEl') contentElRef?: ElementRef<HTMLElement>;
140
+ @ViewChild('overlayEl') overlayElRef?: ElementRef<HTMLElement>;
141
+ @ViewChild('closeEl') closeElRef?: ElementRef<HTMLButtonElement>;
142
+
143
+ api!: ModalConnectReturn;
144
+ titleId = `smui-modal-title-${Date.now()}`;
145
+
146
+ private adapter!: MachineAdapter<ModalState, ModalContext, ModalEvent>;
147
+
148
+ constructor(
149
+ private ngZone: NgZone,
150
+ private cdr: ChangeDetectorRef,
151
+ ) {}
152
+
153
+ ngOnInit(): void {
154
+ this.adapter = MachineAdapter.create(
155
+ this.ngZone,
156
+ createModalMachine({
157
+ closeOnOverlayClick: this.closeOnOverlayClick,
158
+ closeOnEscape: this.closeOnEscape,
159
+ }),
160
+ );
161
+ this.updateApi();
162
+ }
163
+
164
+ ngOnChanges(changes: SimpleChanges): void {
165
+ // Modal machine doesn't have dynamic context updates for these,
166
+ // but we store them for local use in event handlers.
167
+ }
168
+
169
+ ngOnDestroy(): void {
170
+ this.adapter?.destroy();
171
+ }
172
+
173
+ /** Open the modal programmatically. */
174
+ open(): void {
175
+ this.adapter.send({ type: 'OPEN' });
176
+ this.updateApi();
177
+ this.openChange.emit(true);
178
+ this.cdr.markForCheck();
179
+ }
180
+
181
+ /** Close the modal programmatically. */
182
+ close(): void {
183
+ this.adapter.send({ type: 'CLOSE' });
184
+ this.updateApi();
185
+ this.openChange.emit(false);
186
+ this.cdr.markForCheck();
187
+ }
188
+
189
+ onOverlayClick(event: MouseEvent): void {
190
+ if (this.closeOnOverlayClick) {
191
+ this.close();
192
+ }
193
+ }
194
+
195
+ @HostListener('document:keydown', ['$event'])
196
+ handleKeydown(event: KeyboardEvent): void {
197
+ if (this.closeOnEscape && event.key === 'Escape' && this.api.isOpen) {
198
+ this.close();
199
+ }
200
+ }
201
+
202
+ private updateApi(): void {
203
+ this.api = connectModal({
204
+ state: this.adapter.state,
205
+ context: this.adapter.context,
206
+ send: (event) => {
207
+ this.adapter.send(event as ModalEvent);
208
+ this.updateApi();
209
+ this.cdr.markForCheck();
210
+ },
211
+ onOpenChange: (isOpen: boolean) => {
212
+ this.openChange.emit(isOpen);
213
+ },
214
+ });
215
+ }
216
+ }
@@ -0,0 +1,120 @@
1
+ import {
2
+ Component,
3
+ Input,
4
+ OnInit,
5
+ OnChanges,
6
+ SimpleChanges,
7
+ ElementRef,
8
+ Renderer2,
9
+ ViewChild,
10
+ ChangeDetectionStrategy,
11
+ } from '@angular/core';
12
+ import { CommonModule } from '@angular/common';
13
+ import {
14
+ connectTypography,
15
+ type TypographyConnectReturn,
16
+ type TypographyConnectOptions,
17
+ type TypographyVariant,
18
+ } from '@smuikit/core';
19
+
20
+ @Component({
21
+ selector: 'smui-typography',
22
+ standalone: true,
23
+ imports: [CommonModule],
24
+ changeDetection: ChangeDetectionStrategy.OnPush,
25
+ template: `
26
+ <ng-container [ngSwitch]="element">
27
+ <h1 *ngSwitchCase="'h1'" #textEl class="smui-typography" [attr.data-variant]="variant">
28
+ <ng-container *ngTemplateOutlet="content"></ng-container>
29
+ </h1>
30
+ <h2 *ngSwitchCase="'h2'" #textEl class="smui-typography" [attr.data-variant]="variant">
31
+ <ng-container *ngTemplateOutlet="content"></ng-container>
32
+ </h2>
33
+ <h3 *ngSwitchCase="'h3'" #textEl class="smui-typography" [attr.data-variant]="variant">
34
+ <ng-container *ngTemplateOutlet="content"></ng-container>
35
+ </h3>
36
+ <h4 *ngSwitchCase="'h4'" #textEl class="smui-typography" [attr.data-variant]="variant">
37
+ <ng-container *ngTemplateOutlet="content"></ng-container>
38
+ </h4>
39
+ <h5 *ngSwitchCase="'h5'" #textEl class="smui-typography" [attr.data-variant]="variant">
40
+ <ng-container *ngTemplateOutlet="content"></ng-container>
41
+ </h5>
42
+ <h6 *ngSwitchCase="'h6'" #textEl class="smui-typography" [attr.data-variant]="variant">
43
+ <ng-container *ngTemplateOutlet="content"></ng-container>
44
+ </h6>
45
+ <span *ngSwitchCase="'span'" #textEl class="smui-typography" [attr.data-variant]="variant">
46
+ <ng-container *ngTemplateOutlet="content"></ng-container>
47
+ </span>
48
+ <p *ngSwitchDefault #textEl class="smui-typography" [attr.data-variant]="variant">
49
+ <ng-container *ngTemplateOutlet="content"></ng-container>
50
+ </p>
51
+ </ng-container>
52
+
53
+ <ng-template #content>
54
+ <ng-content></ng-content>
55
+ </ng-template>
56
+ `,
57
+ styles: [
58
+ `
59
+ :host {
60
+ display: block;
61
+ }
62
+ `,
63
+ ],
64
+ })
65
+ export class TypographyComponent implements OnInit, OnChanges {
66
+ @Input() variant: TypographyVariant = 'body1';
67
+ @Input() align?: 'left' | 'center' | 'right';
68
+ @Input() truncate = false;
69
+ @Input() color?: string;
70
+
71
+ element = 'p';
72
+
73
+ private api!: TypographyConnectReturn;
74
+
75
+ @ViewChild('textEl', { static: false })
76
+ set textElRef(ref: ElementRef<HTMLElement> | undefined) {
77
+ if (ref) {
78
+ this.applyStyles(ref.nativeElement);
79
+ }
80
+ }
81
+
82
+ constructor(private renderer: Renderer2) {}
83
+
84
+ ngOnInit(): void {
85
+ this.updateApi();
86
+ }
87
+
88
+ ngOnChanges(changes: SimpleChanges): void {
89
+ this.updateApi();
90
+ }
91
+
92
+ private updateApi(): void {
93
+ this.api = connectTypography({
94
+ variant: this.variant,
95
+ align: this.align,
96
+ truncate: this.truncate,
97
+ color: this.color,
98
+ });
99
+ this.element = this.api.element;
100
+ }
101
+
102
+ private applyStyles(el: HTMLElement): void {
103
+ if (!this.api) return;
104
+
105
+ const rootProps = this.api.rootProps;
106
+
107
+ // Apply data attributes
108
+ for (const [key, value] of Object.entries(rootProps)) {
109
+ if (key === 'style' || value === undefined) continue;
110
+ this.renderer.setAttribute(el, key, String(value));
111
+ }
112
+
113
+ // Apply styles
114
+ if (rootProps.style && typeof rootProps.style === 'object') {
115
+ for (const [prop, value] of Object.entries(rootProps.style)) {
116
+ this.renderer.setStyle(el, prop, String(value));
117
+ }
118
+ }
119
+ }
120
+ }
@@ -0,0 +1,112 @@
1
+ import type { ElementProps } from '@smuikit/core';
2
+
3
+ /**
4
+ * Map of React-style event-handler names to their Angular attribute-binding
5
+ * equivalents. Angular templates bind DOM events with `(eventname)`, but
6
+ * when we spread props programmatically in `ngOnChanges` / renderers we
7
+ * need the raw DOM event name so we can call `addEventListener` / set the
8
+ * property on the native element.
9
+ */
10
+ const EVENT_HANDLER_MAP: Record<string, string> = {
11
+ onClick: 'click',
12
+ onPointerDown: 'pointerdown',
13
+ onPointerUp: 'pointerup',
14
+ onFocus: 'focus',
15
+ onBlur: 'blur',
16
+ onKeyDown: 'keydown',
17
+ onChange: 'input', // DOM `input` event is the Angular equivalent of React `onChange`
18
+ };
19
+
20
+ /** Property names that are boolean HTML attributes. */
21
+ const BOOLEAN_ATTRS = new Set([
22
+ 'disabled',
23
+ 'readOnly',
24
+ 'required',
25
+ 'hidden',
26
+ ]);
27
+
28
+ export interface NormalizedProps {
29
+ /** Standard HTML attributes (strings / booleans) to set on the element. */
30
+ attrs: Record<string, string | boolean | number | undefined>;
31
+ /** Style object to apply via Renderer2.setStyle(). */
32
+ styles: Record<string, string | number>;
33
+ /** DOM event listeners keyed by lowercase event name (e.g. 'click'). */
34
+ listeners: Record<string, (event: unknown) => void>;
35
+ }
36
+
37
+ /**
38
+ * Takes an `ElementProps` object produced by a `connect*` function from
39
+ * @smuikit/core and splits it into three buckets that Angular can consume:
40
+ * attributes, styles, and event listeners.
41
+ *
42
+ * - `undefined` values are filtered out.
43
+ * - React-style event handlers (`onClick`, `onFocus`, etc.) are mapped to
44
+ * their DOM event names (`click`, `focus`, ...).
45
+ */
46
+ export function normalizeProps(props: ElementProps | undefined): NormalizedProps {
47
+ const attrs: Record<string, string | boolean | number | undefined> = {};
48
+ const styles: Record<string, string | number> = {};
49
+ const listeners: Record<string, (event: unknown) => void> = {};
50
+
51
+ if (!props) {
52
+ return { attrs, styles, listeners };
53
+ }
54
+
55
+ for (const [key, value] of Object.entries(props)) {
56
+ // Skip undefined values
57
+ if (value === undefined) continue;
58
+
59
+ // Style object
60
+ if (key === 'style' && typeof value === 'object' && value !== null) {
61
+ Object.assign(styles, value);
62
+ continue;
63
+ }
64
+
65
+ // Event handlers
66
+ const eventName = EVENT_HANDLER_MAP[key];
67
+ if (eventName && typeof value === 'function') {
68
+ listeners[eventName] = value as (event: unknown) => void;
69
+ continue;
70
+ }
71
+
72
+ // Regular attributes
73
+ attrs[key] = value as string | boolean | number | undefined;
74
+ }
75
+
76
+ return { attrs, styles, listeners };
77
+ }
78
+
79
+ /**
80
+ * Applies NormalizedProps to a native DOM element.
81
+ * Returns an array of cleanup functions (for removing event listeners).
82
+ */
83
+ export function applyProps(
84
+ element: HTMLElement,
85
+ normalized: NormalizedProps,
86
+ ): (() => void)[] {
87
+ const cleanups: (() => void)[] = [];
88
+
89
+ // Set attributes
90
+ for (const [key, value] of Object.entries(normalized.attrs)) {
91
+ if (value === undefined || value === false) {
92
+ element.removeAttribute(key);
93
+ } else if (value === true) {
94
+ element.setAttribute(key, '');
95
+ } else {
96
+ element.setAttribute(key, String(value));
97
+ }
98
+ }
99
+
100
+ // Set styles
101
+ for (const [key, value] of Object.entries(normalized.styles)) {
102
+ (element.style as unknown as Record<string, string>)[key] = String(value);
103
+ }
104
+
105
+ // Add event listeners
106
+ for (const [eventName, handler] of Object.entries(normalized.listeners)) {
107
+ element.addEventListener(eventName, handler);
108
+ cleanups.push(() => element.removeEventListener(eventName, handler));
109
+ }
110
+
111
+ return cleanups;
112
+ }
@@ -0,0 +1,43 @@
1
+ import { NgModule } from '@angular/core';
2
+
3
+ import { ButtonComponent } from './components/button/button.component';
4
+ import { InputComponent } from './components/input/input.component';
5
+ import { ModalComponent } from './components/modal/modal.component';
6
+ import { TypographyComponent } from './components/typography/typography.component';
7
+ import {
8
+ CardComponent,
9
+ CardHeaderComponent,
10
+ CardBodyComponent,
11
+ CardFooterComponent,
12
+ } from './components/card/card.component';
13
+
14
+ /**
15
+ * SmuiModule re-exports every standalone component so consumers who prefer
16
+ * the traditional NgModule approach can simply import this single module.
17
+ *
18
+ * For tree-shakeable imports, use the standalone components directly:
19
+ * `imports: [ButtonComponent]`
20
+ */
21
+ @NgModule({
22
+ imports: [
23
+ ButtonComponent,
24
+ InputComponent,
25
+ ModalComponent,
26
+ TypographyComponent,
27
+ CardComponent,
28
+ CardHeaderComponent,
29
+ CardBodyComponent,
30
+ CardFooterComponent,
31
+ ],
32
+ exports: [
33
+ ButtonComponent,
34
+ InputComponent,
35
+ ModalComponent,
36
+ TypographyComponent,
37
+ CardComponent,
38
+ CardHeaderComponent,
39
+ CardBodyComponent,
40
+ CardFooterComponent,
41
+ ],
42
+ })
43
+ export class SmuiModule {}
@@ -0,0 +1,87 @@
1
+ import { Injectable, Inject, Optional } from '@angular/core';
2
+ import { DOCUMENT } from '@angular/common';
3
+
4
+ /** Shape of a single design-token override. */
5
+ export interface TokenOverride {
6
+ /** CSS custom-property name *without* the leading `--`, e.g. `smui-color-primary`. */
7
+ name: string;
8
+ value: string;
9
+ }
10
+
11
+ /**
12
+ * ThemeService manages design tokens as CSS custom properties on the
13
+ * document's root element (`<html>`). Consumers inject it and call
14
+ * `setTokens()` or `setToken()` to apply a theme at runtime.
15
+ *
16
+ * It is provided in 'root' so there is a single, application-wide instance.
17
+ */
18
+ @Injectable({ providedIn: 'root' })
19
+ export class ThemeService {
20
+ private tokens = new Map<string, string>();
21
+ private rootElement: HTMLElement | null = null;
22
+
23
+ constructor(@Inject(DOCUMENT) private document: Document) {
24
+ this.rootElement = this.document.documentElement;
25
+ }
26
+
27
+ /**
28
+ * Bulk-set multiple design tokens.
29
+ *
30
+ * ```ts
31
+ * themeService.setTokens({
32
+ * 'smui-color-primary': '#6200ee',
33
+ * 'smui-color-secondary': '#03dac6',
34
+ * 'smui-spacing-md': '16px',
35
+ * });
36
+ * ```
37
+ */
38
+ setTokens(tokens: Record<string, string>): void {
39
+ for (const [name, value] of Object.entries(tokens)) {
40
+ this.setToken(name, value);
41
+ }
42
+ }
43
+
44
+ /** Set a single design token. */
45
+ setToken(name: string, value: string): void {
46
+ this.tokens.set(name, value);
47
+ this.applyToken(name, value);
48
+ }
49
+
50
+ /** Remove a single token override, reverting to the default value. */
51
+ removeToken(name: string): void {
52
+ this.tokens.delete(name);
53
+ if (this.rootElement) {
54
+ this.rootElement.style.removeProperty(`--${name}`);
55
+ }
56
+ }
57
+
58
+ /** Remove all token overrides. */
59
+ resetTokens(): void {
60
+ for (const name of this.tokens.keys()) {
61
+ if (this.rootElement) {
62
+ this.rootElement.style.removeProperty(`--${name}`);
63
+ }
64
+ }
65
+ this.tokens.clear();
66
+ }
67
+
68
+ /** Get the current value of a token (returns the override, not the computed). */
69
+ getToken(name: string): string | undefined {
70
+ return this.tokens.get(name);
71
+ }
72
+
73
+ /** Get all currently applied token overrides. */
74
+ getAllTokens(): Record<string, string> {
75
+ const result: Record<string, string> = {};
76
+ for (const [name, value] of this.tokens.entries()) {
77
+ result[name] = value;
78
+ }
79
+ return result;
80
+ }
81
+
82
+ private applyToken(name: string, value: string): void {
83
+ if (this.rootElement) {
84
+ this.rootElement.style.setProperty(`--${name}`, value);
85
+ }
86
+ }
87
+ }
@@ -0,0 +1,51 @@
1
+ import { NgZone } from '@angular/core';
2
+ import type { MachineService } from '@smuikit/core';
3
+
4
+ export class MachineAdapter<
5
+ TState extends string,
6
+ TContext extends object,
7
+ TEvent extends { type: string },
8
+ > {
9
+ private unsubscribe: (() => void) | null = null;
10
+
11
+ snapshot: { state: TState; context: TContext };
12
+
13
+ private constructor(
14
+ private ngZone: NgZone,
15
+ private machine: MachineService<TState, TContext, TEvent>,
16
+ ) {
17
+ this.snapshot = this.machine.getSnapshot();
18
+ this.machine.start();
19
+ this.unsubscribe = this.machine.subscribe(() => {
20
+ this.ngZone.run(() => {
21
+ this.snapshot = this.machine.getSnapshot();
22
+ });
23
+ });
24
+ }
25
+
26
+ static create<
27
+ S extends string,
28
+ C extends object,
29
+ E extends { type: string },
30
+ >(ngZone: NgZone, machine: MachineService<S, C, E>): MachineAdapter<S, C, E> {
31
+ return new MachineAdapter(ngZone, machine);
32
+ }
33
+
34
+ get state(): TState {
35
+ return this.snapshot.state;
36
+ }
37
+
38
+ get context(): TContext {
39
+ return this.snapshot.context;
40
+ }
41
+
42
+ send(event: TEvent): void {
43
+ this.machine.send(event);
44
+ }
45
+
46
+ destroy(): void {
47
+ this.unsubscribe?.();
48
+ this.unsubscribe = null;
49
+ this.machine.stop();
50
+ }
51
+ }
@@ -0,0 +1,21 @@
1
+ // Module
2
+ export { SmuiModule } from './lib/smui.module';
3
+
4
+ // Services
5
+ export { MachineAdapter } from './lib/use-machine.service';
6
+ export { ThemeService } from './lib/theme.service';
7
+
8
+ // Utilities
9
+ export { normalizeProps, applyProps, type NormalizedProps } from './lib/normalize-props';
10
+
11
+ // Components
12
+ export { ButtonComponent } from './lib/components/button/button.component';
13
+ export { InputComponent } from './lib/components/input/input.component';
14
+ export { ModalComponent } from './lib/components/modal/modal.component';
15
+ export { TypographyComponent } from './lib/components/typography/typography.component';
16
+ export {
17
+ CardComponent,
18
+ CardHeaderComponent,
19
+ CardBodyComponent,
20
+ CardFooterComponent,
21
+ } from './lib/components/card/card.component';
@@ -0,0 +1 @@
1
+ import 'jest-preset-angular/setup-jest';
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src",
6
+ "baseUrl": ".",
7
+ "target": "ES2022",
8
+ "module": "ES2022",
9
+ "moduleResolution": "bundler",
10
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
11
+ "experimentalDecorators": true,
12
+ "emitDecoratorMetadata": true,
13
+ "useDefineForClassFields": false,
14
+ "paths": {
15
+ "@smuikit/core": ["../core/src/index.ts"],
16
+ "@smuikit/tokens": ["../tokens/dist/js/tokens.js"]
17
+ }
18
+ },
19
+ "include": ["src/**/*.ts"],
20
+ "exclude": ["src/**/*.spec.ts", "dist", "node_modules"]
21
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "declaration": true,
6
+ "declarationMap": true,
7
+ "sourceMap": true,
8
+ "inlineSources": true,
9
+ "flatModuleOutFile": "smui-angular.js",
10
+ "flatModuleId": "@smuikit/angular"
11
+ },
12
+ "exclude": ["src/**/*.spec.ts", "node_modules", "dist"]
13
+ }