@trineui/cli 0.1.0-beta.1 → 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.
Files changed (39) hide show
  1. package/README.md +40 -0
  2. package/bin/trine.js +24 -0
  3. package/dist/add-button.js +15 -0
  4. package/dist/add-component.js +315 -0
  5. package/dist/index.js +73 -646
  6. package/package.json +23 -30
  7. package/templates/button/button.html +11 -0
  8. package/templates/button/button.skin.ts +128 -0
  9. package/templates/button/button.ts +39 -0
  10. package/templates/styles/tokens.css +102 -0
  11. package/templates/styles/trine-consumer.css +58 -0
  12. package/CHANGELOG.md +0 -30
  13. package/src/commands/add.ts +0 -101
  14. package/src/commands/diff.test.ts +0 -55
  15. package/src/commands/diff.ts +0 -104
  16. package/src/commands/eject.ts +0 -95
  17. package/src/commands/init.ts +0 -92
  18. package/src/commands/sync-interactive.ts +0 -108
  19. package/src/commands/sync.test.ts +0 -35
  20. package/src/commands/sync.ts +0 -113
  21. package/src/index.ts +0 -18
  22. package/src/types/manifest.ts +0 -14
  23. package/src/utils/__tests__/hash.test.ts +0 -35
  24. package/src/utils/__tests__/template.test.ts +0 -47
  25. package/src/utils/eject-merger.ts +0 -149
  26. package/src/utils/hash.ts +0 -43
  27. package/src/utils/manifest.ts +0 -43
  28. package/src/utils/template.ts +0 -26
  29. package/templates/button.blueprint.ts.hbs +0 -41
  30. package/templates/button.skin.ts.hbs +0 -35
  31. package/templates/checkbox.blueprint.ts.hbs +0 -57
  32. package/templates/checkbox.skin.ts.hbs +0 -44
  33. package/templates/dialog.blueprint.ts.hbs +0 -61
  34. package/templates/dialog.skin.ts.hbs +0 -27
  35. package/templates/input.blueprint.ts.hbs +0 -83
  36. package/templates/input.skin.ts.hbs +0 -29
  37. package/templates/select.blueprint.ts.hbs +0 -86
  38. package/templates/select.skin.ts.hbs +0 -53
  39. package/tsconfig.json +0 -10
@@ -1,149 +0,0 @@
1
- import { Project, SourceFile } from "ts-morph";
2
-
3
- export function mergeForEject(
4
- component: string,
5
- blueprintContent: string,
6
- skinContent: string
7
- ): string {
8
- try {
9
- const project = new Project({ useInMemoryFileSystem: true });
10
-
11
- const blueprintFile = project.createSourceFile(
12
- "blueprint.ts",
13
- blueprintContent
14
- );
15
-
16
- const componentClass = blueprintFile.getClass(
17
- `${capitalize(component)}Component`
18
- );
19
-
20
- if (!componentClass) {
21
- return structuredFallback(component, blueprintContent, skinContent);
22
- }
23
-
24
- const date = new Date().toISOString().split("T")[0];
25
- const lines: string[] = [
26
- `// Ejected from Trine on ${date}.`,
27
- `// This component is no longer managed by trine sync.`,
28
- `// @trine/engine is still required as a dependency.`,
29
- "",
30
- ];
31
-
32
- const imports = collectImports(blueprintFile);
33
- lines.push(...imports);
34
-
35
- lines.push("");
36
- lines.push("@Component({");
37
-
38
- const decoratorProps = extractDecoratorProps(componentClass, component);
39
- lines.push(...decoratorProps);
40
-
41
- lines.push("})");
42
- lines.push(`export class ${capitalize(component)}Component {`);
43
- lines.push("");
44
- lines.push(` protected engine = inject(${capitalize(component)}Engine);`);
45
- lines.push(` protected skin = inject(${capitalize(component)}Skin);`);
46
- lines.push("}");
47
- lines.push("");
48
-
49
- return lines.join("\n");
50
- } catch {
51
- return structuredFallback(component, blueprintContent, skinContent);
52
- }
53
- }
54
-
55
- function capitalize(s: string): string {
56
- return s.charAt(0).toUpperCase() + s.slice(1);
57
- }
58
-
59
- function collectImports(
60
- blueprintFile: SourceFile
61
- ): string[] {
62
- const imports: string[] = [];
63
-
64
- imports.push(
65
- `import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';`
66
- );
67
-
68
- const engineImport = blueprintFile
69
- .getImportDeclarations()
70
- .find((imp) =>
71
- imp.getModuleSpecifier().getText().match(/@trine(ui)?\/engine/)
72
- );
73
-
74
- if (engineImport) {
75
- imports.push(engineImport.getText().replace(/"/g, "'"));
76
- }
77
-
78
- blueprintFile.getImportDeclarations().forEach((imp) => {
79
- const moduleName = imp.getModuleSpecifier().getText();
80
- if (
81
- moduleName.includes("class-variance-authority")
82
- ) {
83
- imports.push(imp.getText());
84
- }
85
- if (
86
- moduleName.includes("./") && moduleName.includes(".skin")
87
- ) {
88
- imports.push(imp.getText());
89
- }
90
- });
91
-
92
- return imports;
93
- }
94
-
95
- function extractDecoratorProps(
96
- componentClass: NonNullable<ReturnType<SourceFile["getClass"]>>,
97
- component: string
98
- ): string[] {
99
- const props: string[] = [];
100
- props.push(` selector: "ui-${component}",`);
101
- props.push(" standalone: true,");
102
-
103
- const decoratorSource = componentClass.getText();
104
- const templateMatch = decoratorSource.match(/template:\s*`[^`]*`/s);
105
- if (templateMatch) {
106
- props.push(` ${templateMatch[0]},`);
107
- }
108
-
109
- const cdMatch = decoratorSource.match(/changeDetection:\s*ChangeDetectionStrategy\.\w+/);
110
- if (cdMatch) {
111
- props.push(` ${cdMatch[0]},`);
112
- }
113
-
114
- const hostMatch = decoratorSource.match(/host:\s*\{[\s\S]*?\n\s*\}/);
115
- if (hostMatch) {
116
- props.push(` ${hostMatch[0]},`);
117
- }
118
-
119
- const hostDirectivesMatch = decoratorSource.match(/hostDirectives:\s*\[[\s\S]*?\n\s*\]/);
120
- if (hostDirectivesMatch) {
121
- props.push(` ${hostDirectivesMatch[0]},`);
122
- }
123
-
124
- const providersMatch = decoratorSource.match(/providers:\s*\[[\s\S]*?\n\s*\]/);
125
- if (providersMatch) {
126
- props.push(` ${providersMatch[0]},`);
127
- }
128
-
129
- return props;
130
- }
131
-
132
- function structuredFallback(
133
- _component: string,
134
- blueprintContent: string,
135
- skinContent: string
136
- ): string {
137
- const date = new Date().toISOString().split("T")[0];
138
- return [
139
- `// Ejected from Trine on ${date}.`,
140
- `// ts-morph merge failed — using structured fallback.`,
141
- `// Merge manually using the sections below.`,
142
- "",
143
- `// ===== BLUEPRINT =====`,
144
- blueprintContent,
145
- "",
146
- `// ===== SKIN =====`,
147
- skinContent,
148
- ].join("\n\n");
149
- }
package/src/utils/hash.ts DELETED
@@ -1,43 +0,0 @@
1
- import { createHash } from 'crypto';
2
-
3
- export function normalizeContent(content: string): string {
4
- let normalized = content
5
- .replace(/\/\/.*$/gm, '')
6
- .replace(/\/\*[\s\S]*?\*\//g, '')
7
- .replace(/'([^']*)'/g, '"$1"')
8
- .replace(/`[^`]*`/g, '``')
9
- .replace(/\s+/g, ' ');
10
-
11
- normalized = normalized
12
- .replace(/,\s*}/g, '}')
13
- .replace(/,\s*]/g, ']')
14
- .replace(/,\s*\)/g, ')')
15
- .replace(/\[\s*/g, '[')
16
- .replace(/\s*\]/g, ']')
17
- .replace(/\(\s*/g, '(')
18
- .replace(/\s+\)/g, ')')
19
- .replace(/{\s*/g, '{')
20
- .replace(/\s*}/g, '}')
21
- .replace(/{\s+}/g, '{}')
22
- .replace(/\[\s+\]/g, '[]')
23
- .replace(/\(\s+\)/g, '()')
24
- .replace(/,\s*/g, ',')
25
- .replace(/:\s*/g, ':');
26
-
27
- const statements = normalized
28
- .split(';')
29
- .map(s => s.trim())
30
- .filter(s => s.length > 0);
31
-
32
- const importStatements = statements.filter(s => s.startsWith('import'));
33
- const otherStatements = statements.filter(s => !s.startsWith('import'));
34
-
35
- importStatements.sort((a, b) => a.localeCompare(b));
36
-
37
- return [...importStatements, ...otherStatements].join('; ') + ';';
38
- }
39
-
40
- export function computeNormalizedHash(content: string): string {
41
- const normalized = normalizeContent(content);
42
- return 'sha256-normalized:' + createHash('sha256').update(normalized).digest('hex');
43
- }
@@ -1,43 +0,0 @@
1
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
2
- import { resolve, dirname } from 'path';
3
- import { TrineManifest } from '../types/manifest.js';
4
-
5
- const TRINE_DIR = '.trine';
6
- const MANIFEST_FILE = 'manifest.json';
7
-
8
- function getManifestPath(projectRoot: string): string {
9
- return resolve(projectRoot, TRINE_DIR, MANIFEST_FILE);
10
- }
11
-
12
- export function readManifest(projectRoot: string): TrineManifest | null {
13
- const manifestPath = getManifestPath(projectRoot);
14
-
15
- if (!existsSync(manifestPath)) {
16
- return null;
17
- }
18
-
19
- try {
20
- const content = readFileSync(manifestPath, 'utf-8');
21
- return JSON.parse(content) as TrineManifest;
22
- } catch {
23
- return null;
24
- }
25
- }
26
-
27
- export function writeManifest(manifest: TrineManifest, projectRoot: string): void {
28
- const manifestPath = getManifestPath(projectRoot);
29
- const dir = dirname(manifestPath);
30
-
31
- if (!existsSync(dir)) {
32
- mkdirSync(dir, { recursive: true });
33
- }
34
-
35
- writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf-8');
36
- }
37
-
38
- export function createDefaultManifest(): TrineManifest {
39
- return {
40
- 'trine-spec': '1.0',
41
- components: {},
42
- };
43
- }
@@ -1,26 +0,0 @@
1
- import Handlebars from 'handlebars';
2
- import { readFileSync, existsSync } from 'fs';
3
- import { resolve } from 'path';
4
-
5
- function findMonorepoRoot(cwd: string): string {
6
- let dir = cwd;
7
- for (;;) {
8
- if (existsSync(resolve(dir, 'pnpm-workspace.yaml'))) {
9
- return dir;
10
- }
11
- const parent = resolve(dir, '..');
12
- if (parent === dir) break;
13
- dir = parent;
14
- }
15
- return cwd;
16
- }
17
-
18
- const monorepoRoot = findMonorepoRoot(process.cwd());
19
- const templatesDir = resolve(monorepoRoot, 'packages/cli/templates');
20
-
21
- export function renderTemplate(templateName: string, context: object): string {
22
- const templatePath = resolve(templatesDir, templateName);
23
- const templateSource = readFileSync(templatePath, 'utf-8');
24
- const template = Handlebars.compile(templateSource);
25
- return template(context);
26
- }
@@ -1,41 +0,0 @@
1
- // @trine-generated
2
- // @trine-version: {{engineVersion}}
3
- // @trine-schema: {{blueprintSchemaVersion}}
4
- import { Component, ChangeDetectionStrategy, inject } from '@angular/core';
5
- import { ButtonEngine } from '@trineui/engine/button';
6
- import { ButtonSkin } from './button.skin';
7
- import { BUTTON_STYLING_CONTRACT } from '@trineui/engine/button';
8
-
9
- @Component({
10
- selector: 'ui-button',
11
- standalone: true,
12
- hostDirectives: [
13
- {
14
- directive: ButtonEngine,
15
- inputs: ['variant', 'size', 'disabled', 'loading'],
16
- outputs: ['pressed'],
17
- },
18
- ],
19
- providers: [
20
- ButtonSkin,
21
- {
22
- provide: BUTTON_STYLING_CONTRACT,
23
- useExisting: ButtonEngine,
24
- },
25
- ],
26
- template: `
27
- @if (engine.loading()) {
28
- <span class="animate-spin mr-2">⏳</span>
29
- }
30
- <ng-content />
31
- `,
32
- host: {
33
- '[class]': 'skin.classes()',
34
- '[attr.data-state]': 'engine.state()',
35
- },
36
- changeDetection: ChangeDetectionStrategy.OnPush,
37
- })
38
- export class ButtonComponent {
39
- protected engine = inject(ButtonEngine);
40
- protected skin = inject(ButtonSkin);
41
- }
@@ -1,35 +0,0 @@
1
- import { Injectable, inject, computed } from '@angular/core';
2
- import { cva } from 'class-variance-authority';
3
- import { BUTTON_STYLING_CONTRACT } from '@trineui/engine/button';
4
-
5
- const buttonVariants = cva(
6
- 'inline-flex items-center justify-center font-semibold transition-all duration-200',
7
- {
8
- variants: {
9
- variant: {
10
- solid: 'bg-primary text-white shadow-md hover:-translate-y-px',
11
- outline: 'border-2 border-primary text-primary hover:bg-primary hover:text-white',
12
- ghost: 'text-primary hover:bg-primary/10',
13
- brand: 'bg-gradient-to-r from-violet-600 to-pink-500 text-white shadow-lg hover:-translate-y-px',
14
- },
15
- size: {
16
- sm: 'h-8 px-3 text-xs rounded-lg',
17
- md: 'h-10 px-5 text-sm rounded-xl',
18
- lg: 'h-12 px-7 text-base rounded-2xl',
19
- },
20
- },
21
- defaultVariants: { variant: 'solid', size: 'md' },
22
- }
23
- );
24
-
25
- @Injectable()
26
- export class ButtonSkin {
27
- private contract = inject(BUTTON_STYLING_CONTRACT);
28
-
29
- readonly classes = computed(() =>
30
- buttonVariants({
31
- variant: this.contract.variant(),
32
- size: this.contract.size(),
33
- })
34
- );
35
- }
@@ -1,57 +0,0 @@
1
- // @trine-generated
2
- // @trine-version: {{engineVersion}}
3
- // @trine-schema: {{blueprintSchemaVersion}}
4
- import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
5
- import { CheckboxEngine } from '@trineui/engine/checkbox';
6
- import { CheckboxSkin } from './checkbox.skin';
7
- import { CHECKBOX_STYLING_CONTRACT } from '@trineui/engine/checkbox';
8
-
9
- @Component({
10
- selector: 'ui-checkbox',
11
- standalone: true,
12
- providers: [
13
- CheckboxEngine,
14
- {
15
- provide: CHECKBOX_STYLING_CONTRACT,
16
- useExisting: CheckboxEngine,
17
- },
18
- ],
19
- template: `
20
- <button
21
- type="button"
22
- role="checkbox"
23
- class="checkbox-host"
24
- [attr.aria-checked]="engine.ariaChecked()"
25
- [attr.aria-disabled]="engine.ariaDisabled()"
26
- [attr.aria-required]="engine.ariaRequired()"
27
- [attr.data-state]="engine.checkboxState()"
28
- [disabled]="engine.disabled()"
29
- (click)="engine.toggle()"
30
- (keydown.space)="$event.preventDefault(); engine.toggle()"
31
- >
32
- <span class="checkbox-indicator" [attr.data-state]="engine.checkboxState()">
33
- @if (engine.checkboxState() === 'checked') {
34
- <svg class="checkbox-icon" viewBox="0 0 16 16" fill="none">
35
- <path d="M3.5 8.5L6.5 11.5L12.5 4.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
36
- </svg>
37
- }
38
- @if (engine.checkboxState() === 'indeterminate') {
39
- <svg class="checkbox-icon" viewBox="0 0 16 16" fill="none">
40
- <path d="M4 8H12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
41
- </svg>
42
- }
43
- </span>
44
- <span class="checkbox-label">
45
- <ng-content></ng-content>
46
- </span>
47
- </button>
48
- `,
49
- host: {
50
- '[class]': 'skin.classes()',
51
- },
52
- changeDetection: ChangeDetectionStrategy.OnPush,
53
- })
54
- export class CheckboxComponent {
55
- protected engine = inject(CheckboxEngine);
56
- protected skin = inject(CheckboxSkin);
57
- }
@@ -1,44 +0,0 @@
1
- // @trine-owned
2
- // DO NOT EDIT — owned by user, never synced
3
- import { cva } from 'class-variance-authority';
4
- import { inject, Injectable } from '@angular/core';
5
- import { CHECKBOX_STYLING_CONTRACT, CheckboxStylingContract, CheckboxState } from '@trineui/engine/checkbox';
6
-
7
- @Injectable()
8
- export class CheckboxSkin {
9
- private contract = inject(CHECKBOX_STYLING_CONTRACT);
10
-
11
- private checkboxVariants = cva('checkbox-base', {
12
- variants: {
13
- state: {
14
- unchecked: 'checkbox-unchecked',
15
- checked: 'checkbox-checked',
16
- indeterminate: 'checkbox-indeterminate',
17
- },
18
- disabled: {
19
- true: 'checkbox-disabled',
20
- },
21
- },
22
- compoundVariants: [
23
- {
24
- state: 'checked',
25
- disabled: true,
26
- class: 'checkbox-checked-disabled',
27
- },
28
- {
29
- state: 'indeterminate',
30
- disabled: true,
31
- class: 'checkbox-indeterminate-disabled',
32
- },
33
- ],
34
- defaultVariants: {
35
- state: 'unchecked',
36
- },
37
- });
38
-
39
- classes(): string {
40
- const state = this.contract.checkboxState();
41
- const disabled = this.contract.disabled();
42
- return this.checkboxVariants({ state, disabled });
43
- }
44
- }
@@ -1,61 +0,0 @@
1
- // @trine-generated
2
- // @trine-version: {{engineVersion}}
3
- // @trine-schema: {{blueprintSchemaVersion}}
4
- import {
5
- ChangeDetectionStrategy,
6
- Component,
7
- inject,
8
- ViewChild,
9
- ViewContainerRef,
10
- } from '@angular/core';
11
- import { DialogEngine } from '@trineui/engine/dialog';
12
- import { DialogSkin } from './dialog.skin';
13
- import { DIALOG_STYLING_CONTRACT } from '@trineui/engine/dialog';
14
-
15
- @Component({
16
- selector: 'ui-dialog',
17
- standalone: true,
18
- providers: [
19
- DialogEngine,
20
- {
21
- provide: DIALOG_STYLING_CONTRACT,
22
- useExisting: DialogEngine,
23
- },
24
- ],
25
- template: `
26
- <ng-template #dialogContent>
27
- <div
28
- class="dialog-panel"
29
- role="dialog"
30
- aria-modal="true"
31
- [attr.aria-labelledby]="titleId"
32
- [attr.data-state]="engine.dialogState()"
33
- >
34
- <ng-content select="[slot=title]"></ng-content>
35
- <ng-content select="[slot=body]"></ng-content>
36
- <ng-content select="[slot=footer]"></ng-content>
37
- </div>
38
- </ng-template>
39
- `,
40
- host: {
41
- '[class]': 'skin.classes()',
42
- },
43
- changeDetection: ChangeDetectionStrategy.OnPush,
44
- })
45
- export class DialogComponent {
46
- protected engine = inject(DialogEngine);
47
- protected skin = inject(DialogSkin);
48
- protected vcr = inject(ViewContainerRef);
49
- protected titleId = `dialog-title-${Math.random().toString(36).slice(2)}`;
50
-
51
- @ViewChild('dialogContent') dialogContent: any;
52
-
53
- open(): void {
54
- this.engine.setViewContainerRef(this.vcr);
55
- this.engine.open(this.dialogContent);
56
- }
57
-
58
- close(): void {
59
- this.engine.close();
60
- }
61
- }
@@ -1,27 +0,0 @@
1
- // @trine-owned
2
- // DO NOT EDIT — owned by user, never synced
3
- import { cva } from 'class-variance-authority';
4
- import { inject, Injectable } from '@angular/core';
5
- import { DIALOG_STYLING_CONTRACT, DialogStylingContract, DialogState } from '@trineui/engine/dialog';
6
-
7
- @Injectable()
8
- export class DialogSkin {
9
- private contract = inject(DIALOG_STYLING_CONTRACT);
10
-
11
- private dialogVariants = cva('dialog-base', {
12
- variants: {
13
- state: {
14
- open: 'dialog-open',
15
- closed: 'dialog-closed',
16
- },
17
- },
18
- defaultVariants: {
19
- state: 'closed',
20
- },
21
- });
22
-
23
- classes(): string {
24
- const state = this.contract.dialogState();
25
- return this.dialogVariants({ state });
26
- }
27
- }
@@ -1,83 +0,0 @@
1
- // @trine-generated
2
- // @trine-version: {{engineVersion}}
3
- // @trine-schema: {{blueprintSchemaVersion}}
4
- import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
5
- import { InputEngine } from '@trineui/engine/input';
6
- import { InputSkin } from './input.skin';
7
- import { INPUT_STYLING_CONTRACT } from '@trineui/engine/input';
8
-
9
- @Component({
10
- selector: 'ui-input',
11
- standalone: true,
12
- hostDirectives: [
13
- {
14
- directive: InputEngine,
15
- inputs: ['value', 'disabled', 'readonly', 'required', 'placeholder', 'errorMessage'],
16
- outputs: ['valueChange'],
17
- },
18
- ],
19
- providers: [
20
- InputSkin,
21
- {
22
- provide: INPUT_STYLING_CONTRACT,
23
- useExisting: InputEngine,
24
- },
25
- ],
26
- template: `
27
- <div class="input-wrapper" [attr.data-state]="engine.inputState()">
28
- @if (engine.hasPrefix()) {
29
- <div class="input-prefix">
30
- <ng-content select="[slot=prefix]"></ng-content>
31
- </div>
32
- }
33
-
34
- <input
35
- class="input-field"
36
- [value]="engine.value()"
37
- [disabled]="engine.disabled()"
38
- [readOnly]="engine.readonly()"
39
- [placeholder]="engine.placeholder()"
40
- [attr.aria-invalid]="engine.ariaInvalid()"
41
- [attr.aria-required]="engine.ariaRequired()"
42
- [attr.aria-describedby]="hintId"
43
- (input)="onInput($event)"
44
- (focus)="engine.isFocused.set(true)"
45
- (blur)="onBlur()"
46
- />
47
-
48
- @if (engine.hasSuffix()) {
49
- <div class="input-suffix">
50
- <ng-content select="[slot=suffix]"></ng-content>
51
- </div>
52
- }
53
- </div>
54
-
55
- @if (engine.hasHint() || engine.isInvalid()) {
56
- <div [id]="hintId" class="input-hint">
57
- <ng-content select="[slot=hint]"></ng-content>
58
- @if (engine.isInvalid() && engine.errorMessage()) {
59
- <span class="input-error">\{{ engine.errorMessage() }}</span>
60
- }
61
- </div>
62
- }
63
- `,
64
- host: {
65
- '[class]': 'skin.classes()',
66
- },
67
- changeDetection: ChangeDetectionStrategy.OnPush,
68
- })
69
- export class InputComponent {
70
- protected engine = inject(InputEngine);
71
- protected skin = inject(InputSkin);
72
- protected hintId = `input-hint-${Math.random().toString(36).slice(2)}`;
73
-
74
- onInput(event: Event): void {
75
- const target = event.target as HTMLInputElement;
76
- this.engine.value.set(target.value);
77
- }
78
-
79
- onBlur(): void {
80
- this.engine.isFocused.set(false);
81
- this.engine.markTouched();
82
- }
83
- }
@@ -1,29 +0,0 @@
1
- // @trine-owned
2
- // DO NOT EDIT — owned by user, never synced
3
- import { cva } from 'class-variance-authority';
4
- import { inject, Injectable } from '@angular/core';
5
- import { INPUT_STYLING_CONTRACT, InputStylingContract, InputState } from '@trineui/engine/input';
6
-
7
- @Injectable()
8
- export class InputSkin {
9
- private contract = inject(INPUT_STYLING_CONTRACT);
10
-
11
- private inputVariants = cva('input-field-base', {
12
- variants: {
13
- state: {
14
- idle: 'input-idle',
15
- focused: 'input-focused',
16
- error: 'input-error',
17
- disabled: 'input-disabled',
18
- },
19
- },
20
- defaultVariants: {
21
- state: 'idle',
22
- },
23
- });
24
-
25
- classes(): string {
26
- const state = this.contract.inputState();
27
- return this.inputVariants({ state });
28
- }
29
- }