@resolve-components/theme 1.0.0 → 1.2.1
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/accordion/rc-accordion.scss +141 -0
- package/alert/rc-alert.scss +158 -0
- package/autocomplete/rc-autocomplete.scss +64 -0
- package/badge/rc-badge.scss +57 -0
- package/button/rc-button.scss +198 -0
- package/card/rc-card.scss +31 -0
- package/checkbox/rc-checkbox.scss +163 -0
- package/chip/rc-chip.scss +172 -0
- package/datepicker/rc-datepicker.scss +322 -0
- package/dialog/rc-dialog.scss +114 -0
- package/fesm2022/resolve-components-theme.mjs +44 -30
- package/fesm2022/resolve-components-theme.mjs.map +1 -1
- package/file-upload/rc-file-upload.scss +143 -0
- package/icon/rc-icon.scss +47 -0
- package/infinite-scroll/rc-infinite-scroll.scss +54 -0
- package/input/rc-input.scss +156 -0
- package/menu/rc-menu.scss +84 -0
- package/navbar/rc-navbar.scss +112 -0
- package/package.json +16 -9
- package/paginator/rc-paginator.scss +86 -0
- package/progress-bar/rc-progress-bar.scss +88 -0
- package/radio/rc-radio.scss +124 -0
- package/schematics/ng-add/index.js +149 -69
- package/schematics/ng-add/schema.json +15 -1
- package/select/rc-select.scss +197 -0
- package/sidenav/rc-sidenav.scss +154 -0
- package/slider/rc-slider.scss +311 -0
- package/spinner/rc-spinner.scss +106 -0
- package/{src/lib/styles → styles}/theming/_mapping.scss +2 -1
- package/table/rc-table.scss +147 -0
- package/tabs/rc-tabs.scss +155 -0
- package/toast/rc-toast.scss +228 -0
- package/toggle/rc-toggle.scss +138 -0
- package/tooltip/rc-tooltip.scss +34 -0
- package/tree/rc-tree.scss +114 -0
- package/types/resolve-components-theme.d.ts +32 -31
- package/virtual-scroll/rc-virtual-scroll.scss +38 -0
- /package/{src/lib/styles → styles}/_all.scss +0 -0
- /package/{src/lib/styles → styles}/_globals.scss +0 -0
- /package/{src/lib/styles → styles}/_themes.scss +0 -0
- /package/{src/lib/styles → styles}/_theming.scss +0 -0
- /package/{src/lib/styles → styles}/themes/_dark.scss +0 -0
- /package/{src/lib/styles → styles}/themes/_default.scss +0 -0
- /package/{src/lib/styles → styles}/theming/_animation.scss +0 -0
- /package/{src/lib/styles → styles}/theming/_functions.scss +0 -0
- /package/{src/lib/styles → styles}/theming/_get-value.scss +0 -0
- /package/{src/lib/styles → styles}/theming/_install.scss +0 -0
- /package/{src/lib/styles → styles}/theming/_register.scss +0 -0
- /package/{src/lib/styles → styles}/theming/_theming-variables.scss +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
@use '../styles/theming' as *;
|
|
2
|
+
|
|
3
|
+
@include rc-install-component() {
|
|
4
|
+
.rc-paginator {
|
|
5
|
+
display: flex;
|
|
6
|
+
align-items: center;
|
|
7
|
+
flex-wrap: wrap;
|
|
8
|
+
width: 100%;
|
|
9
|
+
gap: rc-theme('paginator-gap');
|
|
10
|
+
padding: rc-theme('paginator-padding');
|
|
11
|
+
font-family: rc-theme('paginator-font-family');
|
|
12
|
+
font-size: rc-theme('paginator-font-size');
|
|
13
|
+
background: rc-theme('paginator-background');
|
|
14
|
+
border-top: 1px solid rc-theme('paginator-border-color');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.rc-paginator-size {
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
gap: 0.5rem;
|
|
21
|
+
margin-right: auto;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.rc-paginator-size-label {
|
|
25
|
+
color: rc-theme('paginator-size-label-color');
|
|
26
|
+
white-space: nowrap;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.rc-paginator-range {
|
|
30
|
+
color: rc-theme('paginator-range-color');
|
|
31
|
+
white-space: nowrap;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.rc-paginator-nav {
|
|
35
|
+
display: flex;
|
|
36
|
+
align-items: center;
|
|
37
|
+
gap: 0.125rem;
|
|
38
|
+
margin-left: auto;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.rc-paginator-ellipsis {
|
|
42
|
+
display: inline-flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
width: 2rem;
|
|
46
|
+
height: 2rem;
|
|
47
|
+
color: rc-theme('paginator-ellipsis-color');
|
|
48
|
+
pointer-events: none;
|
|
49
|
+
user-select: none;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ── Mobile ────────────────────────────────────────────────────────────────
|
|
53
|
+
@media (max-width: 600px) {
|
|
54
|
+
.rc-paginator {
|
|
55
|
+
justify-content: center;
|
|
56
|
+
row-gap: 0.5rem;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Size selector: own row, centered, no auto margin
|
|
60
|
+
.rc-paginator-size {
|
|
61
|
+
flex-basis: 100%;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
margin-right: 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Hide verbose "Rows per page:" label — just show the select
|
|
67
|
+
.rc-paginator-size-label {
|
|
68
|
+
display: none;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Range: own centered row
|
|
72
|
+
.rc-paginator-range {
|
|
73
|
+
flex-basis: 100%;
|
|
74
|
+
text-align: center;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Nav: centered, scrollable if too many page buttons
|
|
78
|
+
.rc-paginator-nav {
|
|
79
|
+
margin-left: 0;
|
|
80
|
+
overflow-x: auto;
|
|
81
|
+
max-width: 100%;
|
|
82
|
+
// thin scrollbar so it doesn't look terrible if scrolling is needed
|
|
83
|
+
scrollbar-width: thin;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
@use '../styles/theming' as *;
|
|
2
|
+
|
|
3
|
+
@include rc-install-component() {
|
|
4
|
+
// ── Track ─────────────────────────────────────────────────────────────────
|
|
5
|
+
.rc-progress-bar {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
gap: 0.375rem;
|
|
9
|
+
width: 100%;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.rc-progress-bar-label {
|
|
13
|
+
display: flex;
|
|
14
|
+
justify-content: space-between;
|
|
15
|
+
font-family: rc-theme('progress-bar-label-font-family');
|
|
16
|
+
font-size: rc-theme('progress-bar-label-font-size');
|
|
17
|
+
color: rc-theme('progress-bar-label-color');
|
|
18
|
+
line-height: 1;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.rc-progress-bar-track {
|
|
22
|
+
position: relative;
|
|
23
|
+
width: 100%;
|
|
24
|
+
height: rc-theme('progress-bar-height');
|
|
25
|
+
background: rc-theme('progress-bar-background');
|
|
26
|
+
border-radius: rc-theme('progress-bar-border-radius');
|
|
27
|
+
overflow: hidden;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ── Fill ──────────────────────────────────────────────────────────────────
|
|
31
|
+
.rc-progress-bar-fill {
|
|
32
|
+
height: 100%;
|
|
33
|
+
border-radius: inherit;
|
|
34
|
+
background: currentColor;
|
|
35
|
+
@include rc-component-animation(
|
|
36
|
+
width,
|
|
37
|
+
$duration: rc-theme('progress-bar-transition-duration')
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── Status colors ────────────────────────────────────────────────────────
|
|
42
|
+
.rc-progress-bar-primary {
|
|
43
|
+
color: rc-theme('progress-bar-primary-color');
|
|
44
|
+
}
|
|
45
|
+
.rc-progress-bar-info {
|
|
46
|
+
color: rc-theme('progress-bar-info-color');
|
|
47
|
+
}
|
|
48
|
+
.rc-progress-bar-success {
|
|
49
|
+
color: rc-theme('progress-bar-success-color');
|
|
50
|
+
}
|
|
51
|
+
.rc-progress-bar-warning {
|
|
52
|
+
color: rc-theme('progress-bar-warning-color');
|
|
53
|
+
}
|
|
54
|
+
.rc-progress-bar-danger {
|
|
55
|
+
color: rc-theme('progress-bar-danger-color');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── Striped ───────────────────────────────────────────────────────────────
|
|
59
|
+
.rc-progress-bar-striped .rc-progress-bar-fill {
|
|
60
|
+
background-image: linear-gradient(
|
|
61
|
+
-45deg,
|
|
62
|
+
rgba(255, 255, 255, 0.18) 25%,
|
|
63
|
+
transparent 25%,
|
|
64
|
+
transparent 50%,
|
|
65
|
+
rgba(255, 255, 255, 0.18) 50%,
|
|
66
|
+
rgba(255, 255, 255, 0.18) 75%,
|
|
67
|
+
transparent 75%,
|
|
68
|
+
transparent
|
|
69
|
+
);
|
|
70
|
+
background-size: 1.5rem 1.5rem;
|
|
71
|
+
background-color: currentColor;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── Indeterminate ─────────────────────────────────────────────────────────
|
|
75
|
+
.rc-progress-bar-indeterminate .rc-progress-bar-fill {
|
|
76
|
+
width: 40% !important;
|
|
77
|
+
animation: rc-progress-indeterminate 1.4s ease-in-out infinite;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@keyframes rc-progress-indeterminate {
|
|
81
|
+
0% {
|
|
82
|
+
transform: translateX(-100%);
|
|
83
|
+
}
|
|
84
|
+
100% {
|
|
85
|
+
transform: translateX(350%);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// rc-radio Resolve Components Radio Button (Themed)
|
|
3
|
+
// =============================================================================
|
|
4
|
+
|
|
5
|
+
@use '../styles/theming/get-value' as *;
|
|
6
|
+
@use '../styles/theming/install' as *;
|
|
7
|
+
@use '../styles/theming/animation' as *;
|
|
8
|
+
|
|
9
|
+
@include rc-install-component() {
|
|
10
|
+
.rc-radio-group {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
gap: rc-theme('radio-group-gap');
|
|
14
|
+
|
|
15
|
+
&-disabled {
|
|
16
|
+
opacity: rc-theme('radio-disabled-opacity');
|
|
17
|
+
pointer-events: none;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.rc-radio-button {
|
|
22
|
+
display: inline-flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
gap: rc-theme('radio-gap');
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
user-select: none;
|
|
27
|
+
font-family: rc-theme('radio-font-family');
|
|
28
|
+
color: rc-theme('radio-text-color');
|
|
29
|
+
|
|
30
|
+
// ── Sizes ──────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
&-sm {
|
|
33
|
+
font-size: rc-theme('radio-sm-font-size');
|
|
34
|
+
|
|
35
|
+
.rc-radio-button-control {
|
|
36
|
+
width: rc-theme('radio-sm-size');
|
|
37
|
+
height: rc-theme('radio-sm-size');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.rc-radio-button-dot {
|
|
41
|
+
width: rc-theme('radio-sm-dot-size');
|
|
42
|
+
height: rc-theme('radio-sm-dot-size');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&-md {
|
|
47
|
+
font-size: rc-theme('radio-md-font-size');
|
|
48
|
+
|
|
49
|
+
.rc-radio-button-control {
|
|
50
|
+
width: rc-theme('radio-md-size');
|
|
51
|
+
height: rc-theme('radio-md-size');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.rc-radio-button-dot {
|
|
55
|
+
width: rc-theme('radio-md-dot-size');
|
|
56
|
+
height: rc-theme('radio-md-dot-size');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── Disabled ─────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
&-disabled {
|
|
63
|
+
opacity: rc-theme('radio-disabled-opacity');
|
|
64
|
+
cursor: not-allowed;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ── Checked ──────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
&-checked .rc-radio-button-control {
|
|
70
|
+
border-color: rc-theme('radio-checked-border-color');
|
|
71
|
+
background: rc-theme('radio-checked-background');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
&-checked .rc-radio-button-dot {
|
|
75
|
+
background: rc-theme('radio-dot-color');
|
|
76
|
+
transform: scale(1);
|
|
77
|
+
opacity: 1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ── Focus ring ────────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
&:focus-visible .rc-radio-button-control {
|
|
83
|
+
outline: 2px solid rc-theme('radio-focus-outline-color');
|
|
84
|
+
outline-offset: 2px;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ── Hover (not disabled, not checked) ─────────────────────────────────
|
|
88
|
+
|
|
89
|
+
&:not(.rc-radio-button-disabled):not(.rc-radio-button-checked):hover {
|
|
90
|
+
.rc-radio-button-control {
|
|
91
|
+
border-color: rc-theme('radio-hover-border-color');
|
|
92
|
+
background: rc-theme('radio-hover-background');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ── Parts ─────────────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
&-control {
|
|
99
|
+
flex-shrink: 0;
|
|
100
|
+
display: flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
justify-content: center;
|
|
103
|
+
border-radius: 50%;
|
|
104
|
+
border: 2px solid rc-theme('radio-border-color');
|
|
105
|
+
background: rc-theme('radio-background');
|
|
106
|
+
@include rc-component-animation(
|
|
107
|
+
border-color background box-shadow,
|
|
108
|
+
$speed: 'medium'
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
&-dot {
|
|
113
|
+
border-radius: 50%;
|
|
114
|
+
background: rc-theme('radio-dot-color');
|
|
115
|
+
transform: scale(0);
|
|
116
|
+
opacity: 0;
|
|
117
|
+
@include rc-component-animation(transform opacity, $speed: 'medium');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
&-label {
|
|
121
|
+
line-height: 1.5;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -3,27 +3,101 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ngAdd = ngAdd;
|
|
4
4
|
const schematics_1 = require("@angular-devkit/schematics");
|
|
5
5
|
const tasks_1 = require("@angular-devkit/schematics/tasks");
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Helpers
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
const isNxWorkspace = (tree) => tree.exists('nx.json');
|
|
10
|
+
/**
|
|
11
|
+
* Try to resolve project config from a root angular.json / workspace.json.
|
|
12
|
+
* Works for standard Angular workspaces AND Nx workspaces that still maintain
|
|
13
|
+
* a root angular.json shim.
|
|
14
|
+
*/
|
|
15
|
+
function getAngularWorkspaceProject(tree, projectName) {
|
|
16
|
+
const wsFile = tree.exists('angular.json')
|
|
8
17
|
? 'angular.json'
|
|
9
18
|
: tree.exists('workspace.json')
|
|
10
19
|
? 'workspace.json'
|
|
11
20
|
: null;
|
|
12
|
-
if (!
|
|
21
|
+
if (!wsFile)
|
|
13
22
|
return null;
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
|
+
let ws;
|
|
14
25
|
try {
|
|
15
|
-
|
|
16
|
-
return { file, content };
|
|
26
|
+
ws = JSON.parse(tree.read(wsFile).toString('utf-8'));
|
|
17
27
|
}
|
|
18
28
|
catch {
|
|
19
29
|
return null;
|
|
20
30
|
}
|
|
31
|
+
const resolved = projectName ??
|
|
32
|
+
ws['defaultProject'] ??
|
|
33
|
+
Object.keys(ws['projects'] ?? {})[0] ??
|
|
34
|
+
null;
|
|
35
|
+
if (!resolved)
|
|
36
|
+
return null;
|
|
37
|
+
const project = ws['projects']?.[resolved];
|
|
38
|
+
if (!project)
|
|
39
|
+
return null;
|
|
40
|
+
const sourceRoot = project['sourceRoot'] ?? 'src';
|
|
41
|
+
return {
|
|
42
|
+
file: wsFile,
|
|
43
|
+
projectName: resolved,
|
|
44
|
+
content: ws,
|
|
45
|
+
sourceRoot,
|
|
46
|
+
isNxProjectJson: false,
|
|
47
|
+
};
|
|
21
48
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
49
|
+
/**
|
|
50
|
+
* In Nx v16+ workspaces there is no writable root angular.json.
|
|
51
|
+
* Discover project.json files under apps/ or projects/.
|
|
52
|
+
*/
|
|
53
|
+
function getNxProjectJsonConfig(tree, projectName) {
|
|
54
|
+
const candidates = [];
|
|
55
|
+
if (projectName) {
|
|
56
|
+
candidates.push(`apps/${projectName}/project.json`, `libs/${projectName}/project.json`, `${projectName}/project.json`);
|
|
57
|
+
}
|
|
58
|
+
// Auto-discover under apps/ and projects/
|
|
59
|
+
for (const root of ['apps', 'projects']) {
|
|
60
|
+
const dir = tree.getDir(root);
|
|
61
|
+
for (const sub of dir.subdirs) {
|
|
62
|
+
const p = `${root}/${sub}/project.json`;
|
|
63
|
+
if (!candidates.includes(p))
|
|
64
|
+
candidates.push(p);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const path of candidates) {
|
|
68
|
+
if (!tree.exists(path))
|
|
69
|
+
continue;
|
|
70
|
+
try {
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
72
|
+
const content = JSON.parse(tree.read(path).toString('utf-8'));
|
|
73
|
+
// Skip non-application projects (libraries, e2e, etc.)
|
|
74
|
+
if (content['projectType'] && content['projectType'] !== 'application')
|
|
75
|
+
continue;
|
|
76
|
+
const projName = content['name'] ?? path.split('/')[1];
|
|
77
|
+
const defaultSourceRoot = path.split('/').slice(0, -1).join('/') + '/src';
|
|
78
|
+
const sourceRoot = content['sourceRoot'] ?? defaultSourceRoot;
|
|
79
|
+
return {
|
|
80
|
+
file: path,
|
|
81
|
+
projectName: projName,
|
|
82
|
+
content,
|
|
83
|
+
sourceRoot,
|
|
84
|
+
isNxProjectJson: true,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
function resolveProjectConfig(tree, options) {
|
|
94
|
+
const nx = isNxWorkspace(tree);
|
|
95
|
+
// Prefer reading from project.json in Nx workspaces (it's always writable)
|
|
96
|
+
if (nx) {
|
|
97
|
+
return (getNxProjectJsonConfig(tree, options.project ?? null) ??
|
|
98
|
+
getAngularWorkspaceProject(tree, options.project ?? null));
|
|
99
|
+
}
|
|
100
|
+
return getAngularWorkspaceProject(tree, options.project ?? null);
|
|
27
101
|
}
|
|
28
102
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
103
|
function getBuildOptions(project) {
|
|
@@ -32,100 +106,103 @@ function getBuildOptions(project) {
|
|
|
32
106
|
null);
|
|
33
107
|
}
|
|
34
108
|
// ---------------------------------------------------------------------------
|
|
35
|
-
// Rule: patch angular.json
|
|
109
|
+
// Rule: patch build config (angular.json or Nx project.json)
|
|
36
110
|
// ---------------------------------------------------------------------------
|
|
37
|
-
function
|
|
38
|
-
return (tree) => {
|
|
39
|
-
const
|
|
40
|
-
if (!
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
111
|
+
function addBuildConfig(options) {
|
|
112
|
+
return (tree, ctx) => {
|
|
113
|
+
const cfg = resolveProjectConfig(tree, options);
|
|
114
|
+
if (!cfg) {
|
|
115
|
+
ctx.logger.warn('⚠ Could not locate a build config (angular.json / project.json).\n' +
|
|
116
|
+
' Add the following manually to your project build options:\n' +
|
|
117
|
+
' • styles: ["node_modules/@angular/cdk/overlay-prebuilt.css"]\n' +
|
|
118
|
+
' • stylePreprocessorOptions.includePaths: ["node_modules/@resolve-components/theme/styles"]');
|
|
44
119
|
return tree;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
120
|
+
}
|
|
121
|
+
ctx.logger.info(` Configuring project: ${cfg.projectName} (${cfg.file})`);
|
|
122
|
+
// In angular.json the project lives under projects[name]; in project.json it IS the root
|
|
123
|
+
const buildTarget = cfg.isNxProjectJson
|
|
124
|
+
? cfg.content
|
|
125
|
+
: cfg.content['projects']?.[cfg.projectName];
|
|
126
|
+
const buildOptions = getBuildOptions(buildTarget);
|
|
127
|
+
if (!buildOptions) {
|
|
128
|
+
ctx.logger.warn('⚠ Could not find build target options — add them manually.');
|
|
50
129
|
return tree;
|
|
51
|
-
|
|
130
|
+
}
|
|
131
|
+
// ── styles: add CDK overlay CSS ──
|
|
52
132
|
const cdkStyle = 'node_modules/@angular/cdk/overlay-prebuilt.css';
|
|
53
|
-
if (!Array.isArray(buildOptions['styles']))
|
|
133
|
+
if (!Array.isArray(buildOptions['styles']))
|
|
54
134
|
buildOptions['styles'] = [];
|
|
55
|
-
}
|
|
56
135
|
if (!buildOptions['styles'].includes(cdkStyle)) {
|
|
57
136
|
buildOptions['styles'].unshift(cdkStyle);
|
|
58
137
|
}
|
|
59
138
|
// ── stylePreprocessorOptions: add includePath ──
|
|
60
139
|
const includePath = 'node_modules/@resolve-components/theme/styles';
|
|
61
140
|
buildOptions['stylePreprocessorOptions'] ??= {};
|
|
62
|
-
const paths =
|
|
63
|
-
|
|
141
|
+
const paths = buildOptions['stylePreprocessorOptions']['includePaths'] ?? [];
|
|
142
|
+
buildOptions['stylePreprocessorOptions']['includePaths'] = paths;
|
|
143
|
+
if (!paths.includes(includePath))
|
|
64
144
|
paths.unshift(includePath);
|
|
65
|
-
|
|
66
|
-
tree.overwrite(ws.file, JSON.stringify(ws.content, null, 2));
|
|
145
|
+
tree.overwrite(cfg.file, JSON.stringify(cfg.content, null, 2));
|
|
67
146
|
return tree;
|
|
68
147
|
};
|
|
69
148
|
}
|
|
70
149
|
// ---------------------------------------------------------------------------
|
|
71
150
|
// Rule: add provideRcTheme() to app.config.ts
|
|
72
151
|
// ---------------------------------------------------------------------------
|
|
73
|
-
function addProviderToConfig() {
|
|
74
|
-
return (tree) => {
|
|
75
|
-
const
|
|
76
|
-
|
|
152
|
+
function addProviderToConfig(options) {
|
|
153
|
+
return (tree, ctx) => {
|
|
154
|
+
const cfg = resolveProjectConfig(tree, options);
|
|
155
|
+
const sourceRoot = cfg?.sourceRoot ?? 'src';
|
|
156
|
+
const configPath = `${sourceRoot}/app/app.config.ts`;
|
|
157
|
+
if (!tree.exists(configPath)) {
|
|
158
|
+
ctx.logger.warn(`⚠ ${configPath} not found — add provideRcTheme() manually.`);
|
|
77
159
|
return tree;
|
|
160
|
+
}
|
|
78
161
|
let content = tree.read(configPath).toString('utf-8');
|
|
79
162
|
if (content.includes('provideRcTheme'))
|
|
80
163
|
return tree;
|
|
81
|
-
|
|
164
|
+
const themeName = options.theme ?? 'default';
|
|
165
|
+
// Append import after the last existing import line
|
|
82
166
|
const importLine = `import { provideRcTheme } from '@resolve-components/theme';\n`;
|
|
83
|
-
const
|
|
84
|
-
if (
|
|
85
|
-
const insertAt =
|
|
86
|
-
content =
|
|
167
|
+
const lastImportMatch = [...content.matchAll(/^import .*;\n/gm)].pop();
|
|
168
|
+
if (lastImportMatch) {
|
|
169
|
+
const insertAt = lastImportMatch.index + lastImportMatch[0].length;
|
|
170
|
+
content =
|
|
171
|
+
content.slice(0, insertAt) + importLine + content.slice(insertAt);
|
|
87
172
|
}
|
|
88
173
|
else {
|
|
89
174
|
content = importLine + content;
|
|
90
175
|
}
|
|
91
|
-
//
|
|
92
|
-
content = content.replace(/(providers:\s*\[)/, `$1\n provideRcTheme({ name: '
|
|
176
|
+
// Prepend provider inside the providers array
|
|
177
|
+
content = content.replace(/(providers:\s*\[)/, `$1\n provideRcTheme({ name: '${themeName}' }),`);
|
|
93
178
|
tree.overwrite(configPath, content);
|
|
179
|
+
ctx.logger.info(` Added provideRcTheme({ name: '${themeName}' }) → ${configPath}`);
|
|
94
180
|
return tree;
|
|
95
181
|
};
|
|
96
182
|
}
|
|
97
183
|
// ---------------------------------------------------------------------------
|
|
98
184
|
// Rule: prepend @use / @include to styles.scss
|
|
99
185
|
// ---------------------------------------------------------------------------
|
|
100
|
-
function addGlobalStyles() {
|
|
101
|
-
return (tree) => {
|
|
102
|
-
const
|
|
103
|
-
|
|
186
|
+
function addGlobalStyles(options) {
|
|
187
|
+
return (tree, ctx) => {
|
|
188
|
+
const cfg = resolveProjectConfig(tree, options);
|
|
189
|
+
const sourceRoot = cfg?.sourceRoot ?? 'src';
|
|
190
|
+
const stylesPath = `${sourceRoot}/styles.scss`;
|
|
191
|
+
if (!tree.exists(stylesPath)) {
|
|
192
|
+
ctx.logger.warn(`⚠ ${stylesPath} not found.\n` +
|
|
193
|
+
` Add the following to your global stylesheet:\n` +
|
|
194
|
+
` @use 'all' as *;\n @include rc-install();`);
|
|
104
195
|
return tree;
|
|
196
|
+
}
|
|
105
197
|
let content = tree.read(stylesPath).toString('utf-8');
|
|
106
|
-
if (content.includes('@resolve-components/theme') ||
|
|
198
|
+
if (content.includes('@resolve-components/theme') ||
|
|
199
|
+
content.includes(`@use 'all'`)) {
|
|
107
200
|
return tree;
|
|
108
201
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
`@use 'theming/install' as *;`,
|
|
112
|
-
``,
|
|
113
|
-
`@include rc-install() {`,
|
|
114
|
-
` *,`,
|
|
115
|
-
` *::before,`,
|
|
116
|
-
` *::after { box-sizing: border-box; }`,
|
|
117
|
-
``,
|
|
118
|
-
` body {`,
|
|
119
|
-
` margin: 0;`,
|
|
120
|
-
` font-family: rc-theme('font-family-primary');`,
|
|
121
|
-
` font-size: rc-theme('text-body-font-size');`,
|
|
122
|
-
` color: rc-theme('text-basic-color');`,
|
|
123
|
-
` background-color: rc-theme('background-basic-color-1');`,
|
|
124
|
-
` }`,
|
|
125
|
-
`}`,
|
|
126
|
-
``,
|
|
127
|
-
].join('\n');
|
|
202
|
+
// Minimal two-line setup — rc-install() emits all global resets and CSS vars
|
|
203
|
+
const addition = `@use 'all' as *;\n\n// Emit CSS custom property blocks\n@include rc-install();\n\n`;
|
|
128
204
|
tree.overwrite(stylesPath, addition + content);
|
|
205
|
+
ctx.logger.info(` Updated ${stylesPath} with @use 'all' as * and @include rc-install()`);
|
|
129
206
|
return tree;
|
|
130
207
|
};
|
|
131
208
|
}
|
|
@@ -134,13 +211,16 @@ function addGlobalStyles() {
|
|
|
134
211
|
// ---------------------------------------------------------------------------
|
|
135
212
|
function ngAdd(options) {
|
|
136
213
|
return (tree, context) => {
|
|
137
|
-
|
|
214
|
+
const nx = isNxWorkspace(tree);
|
|
215
|
+
context.logger.info(`\n🚀 Setting up @resolve-components/theme${nx ? ' (Nx monorepo detected)' : ''}…\n`);
|
|
138
216
|
return (0, schematics_1.chain)([
|
|
139
|
-
|
|
140
|
-
addProviderToConfig(),
|
|
141
|
-
addGlobalStyles(),
|
|
217
|
+
addBuildConfig(options),
|
|
218
|
+
addProviderToConfig(options),
|
|
219
|
+
addGlobalStyles(options),
|
|
142
220
|
(_tree, ctx) => {
|
|
143
221
|
ctx.addTask(new tasks_1.NodePackageInstallTask());
|
|
222
|
+
ctx.logger.info('\n✅ @resolve-components/theme setup complete!');
|
|
223
|
+
ctx.logger.info(' Restart your dev server to apply the new Sass includePaths.\n');
|
|
144
224
|
},
|
|
145
225
|
])(tree, context);
|
|
146
226
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema",
|
|
3
|
-
"id": "SchematicsResolveComponentsNgAdd",
|
|
3
|
+
"$id": "SchematicsResolveComponentsNgAdd",
|
|
4
4
|
"title": "Add @resolve-components/theme to an Angular project",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"properties": {
|
|
@@ -10,6 +10,20 @@
|
|
|
10
10
|
"$default": {
|
|
11
11
|
"$source": "projectName"
|
|
12
12
|
}
|
|
13
|
+
},
|
|
14
|
+
"theme": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "The default visual theme to install.",
|
|
17
|
+
"default": "default",
|
|
18
|
+
"enum": ["default", "dark"],
|
|
19
|
+
"x-prompt": {
|
|
20
|
+
"message": "Which default theme would you like to use?",
|
|
21
|
+
"type": "list",
|
|
22
|
+
"items": [
|
|
23
|
+
{ "value": "default", "label": "\u2600\uFE0F Light (default)" },
|
|
24
|
+
{ "value": "dark", "label": "\uD83C\uDF19 Dark" }
|
|
25
|
+
]
|
|
26
|
+
}
|
|
13
27
|
}
|
|
14
28
|
}
|
|
15
29
|
}
|