@tx-angular-design-system/ui-kit 0.1.0 → 0.1.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tx-angular-design-system/ui-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "TraXtion Angular design system — a headless-primitive component library styled entirely through Tailwind CSS v4 design tokens.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -57,4 +57,4 @@
|
|
|
57
57
|
"module": "fesm2022/tx-angular-design-system-ui-kit.mjs",
|
|
58
58
|
"typings": "types/tx-angular-design-system-ui-kit.d.ts",
|
|
59
59
|
"type": "module"
|
|
60
|
-
}
|
|
60
|
+
}
|
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ngAdd = ngAdd;
|
|
4
|
-
/**
|
|
5
|
-
* `ng add @tx-angular-design-system/ui-kit`
|
|
6
|
-
*
|
|
7
|
-
* Brings a consumer app from zero to rendering ui-kit components correctly:
|
|
8
|
-
*
|
|
9
|
-
* 1. adds the runtime peer dependencies and Tailwind v4
|
|
10
|
-
* 2. writes a PostCSS config wiring in @tailwindcss/postcss
|
|
11
|
-
* 3. adds four directives to the app's global stylesheet — the Tailwind
|
|
12
|
-
* import, the CDK overlay stylesheet, the kit's theme, and (critically)
|
|
13
|
-
* an `@source` pointing at the installed package
|
|
14
|
-
* 4. links IBM Plex Sans / IBM Plex Mono / Archivo in index.html
|
|
15
|
-
*
|
|
16
|
-
* Every step is idempotent and refuses to overwrite an existing Tailwind or
|
|
17
|
-
* PostCSS setup — re-running it on a configured app is a no-op.
|
|
18
|
-
*
|
|
19
|
-
* Step 3's `@source` is the load-bearing one. Tailwind's automatic content
|
|
20
|
-
* detection deliberately skips node_modules, so without an explicit @source
|
|
21
|
-
* none of the kit's utility classes are generated from its compiled .mjs
|
|
22
|
-
* templates and every component renders unstyled.
|
|
23
|
-
*/
|
|
24
|
-
const schematics_1 = require("@angular-devkit/schematics");
|
|
25
|
-
const tasks_1 = require("@angular-devkit/schematics/tasks");
|
|
26
|
-
const utility_1 = require("@schematics/angular/utility");
|
|
27
|
-
const PACKAGE_NAME = '@tx-angular-design-system/ui-kit';
|
|
28
|
-
/** Kept in step with the library's own peerDependencies. */
|
|
29
|
-
const PEER_VERSIONS = {
|
|
30
|
-
'@angular/aria': '^22.1.0',
|
|
31
|
-
'@angular/cdk': '^22.1.0',
|
|
32
|
-
tailwindcss: '^4.0.0',
|
|
33
|
-
'@tailwindcss/postcss': '^4.0.0',
|
|
34
|
-
};
|
|
35
|
-
const TAILWIND_IMPORT = `@import 'tailwindcss';`;
|
|
36
|
-
const CDK_IMPORT = `@import '@angular/cdk/overlay-prebuilt.css';`;
|
|
37
|
-
const THEME_IMPORT = `@import '${PACKAGE_NAME}/styles/ui-kit.css';`;
|
|
38
|
-
const FONT_FAMILIES = 'family=Archivo:wght@500;600;700' +
|
|
39
|
-
'&family=IBM+Plex+Mono:wght@400;500' +
|
|
40
|
-
'&family=IBM+Plex+Sans:wght@400;500;600';
|
|
41
|
-
const FONT_HREF = `https://fonts.googleapis.com/css2?${FONT_FAMILIES}&display=swap`;
|
|
42
|
-
const POSTCSS_JSON_CANDIDATES = ['.postcssrc.json', '.postcssrc'];
|
|
43
|
-
const POSTCSS_JS_CANDIDATES = [
|
|
44
|
-
'postcss.config.js',
|
|
45
|
-
'postcss.config.mjs',
|
|
46
|
-
'postcss.config.cjs',
|
|
47
|
-
'postcss.config.ts',
|
|
48
|
-
];
|
|
49
|
-
function ngAdd(options) {
|
|
50
|
-
return (0, schematics_1.chain)([
|
|
51
|
-
(0, utility_1.addDependency)('@angular/cdk', PEER_VERSIONS['@angular/cdk'], {
|
|
52
|
-
type: utility_1.DependencyType.Default,
|
|
53
|
-
existing: utility_1.ExistingBehavior.Skip,
|
|
54
|
-
install: utility_1.InstallBehavior.None,
|
|
55
|
-
}),
|
|
56
|
-
(0, utility_1.addDependency)('@angular/aria', PEER_VERSIONS['@angular/aria'], {
|
|
57
|
-
type: utility_1.DependencyType.Default,
|
|
58
|
-
existing: utility_1.ExistingBehavior.Skip,
|
|
59
|
-
install: utility_1.InstallBehavior.None,
|
|
60
|
-
}),
|
|
61
|
-
(0, utility_1.addDependency)('tailwindcss', PEER_VERSIONS.tailwindcss, {
|
|
62
|
-
type: utility_1.DependencyType.Dev,
|
|
63
|
-
existing: utility_1.ExistingBehavior.Skip,
|
|
64
|
-
install: utility_1.InstallBehavior.None,
|
|
65
|
-
}),
|
|
66
|
-
(0, utility_1.addDependency)('@tailwindcss/postcss', PEER_VERSIONS['@tailwindcss/postcss'], {
|
|
67
|
-
type: utility_1.DependencyType.Dev,
|
|
68
|
-
existing: utility_1.ExistingBehavior.Skip,
|
|
69
|
-
install: utility_1.InstallBehavior.None,
|
|
70
|
-
}),
|
|
71
|
-
ensurePostcssConfig(),
|
|
72
|
-
setUpProject(options),
|
|
73
|
-
scheduleInstall(options),
|
|
74
|
-
]);
|
|
75
|
-
}
|
|
76
|
-
/* -------------------------------------------------------------------------- *
|
|
77
|
-
* PostCSS
|
|
78
|
-
* -------------------------------------------------------------------------- */
|
|
79
|
-
function ensurePostcssConfig() {
|
|
80
|
-
return (tree, context) => {
|
|
81
|
-
const existingJs = POSTCSS_JS_CANDIDATES.find((p) => tree.exists(p));
|
|
82
|
-
if (existingJs) {
|
|
83
|
-
context.logger.info(` ↷ Found ${existingJs}. Leaving it alone — add '@tailwindcss/postcss' to its plugins yourself.`);
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
const existingJson = POSTCSS_JSON_CANDIDATES.find((p) => tree.exists(p));
|
|
87
|
-
const path = existingJson ?? '.postcssrc.json';
|
|
88
|
-
let config = {};
|
|
89
|
-
if (existingJson) {
|
|
90
|
-
const raw = tree.readText(existingJson);
|
|
91
|
-
try {
|
|
92
|
-
config = JSON.parse(raw);
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
context.logger.warn(` ! Could not parse ${existingJson}; add '@tailwindcss/postcss' to its plugins yourself.`);
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
config.plugins ??= {};
|
|
100
|
-
if ('@tailwindcss/postcss' in config.plugins) {
|
|
101
|
-
context.logger.info(` ✓ ${path} already loads @tailwindcss/postcss.`);
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
config.plugins['@tailwindcss/postcss'] = {};
|
|
105
|
-
const content = JSON.stringify(config, null, 2) + '\n';
|
|
106
|
-
if (existingJson) {
|
|
107
|
-
tree.overwrite(path, content);
|
|
108
|
-
context.logger.info(` ✓ Added @tailwindcss/postcss to ${path}.`);
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
tree.create(path, content);
|
|
112
|
-
context.logger.info(` ✓ Created ${path}.`);
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
/* -------------------------------------------------------------------------- *
|
|
117
|
-
* Project wiring
|
|
118
|
-
* -------------------------------------------------------------------------- */
|
|
119
|
-
function setUpProject(options) {
|
|
120
|
-
return async (tree, context) => {
|
|
121
|
-
const workspace = await (0, utility_1.readWorkspace)(tree);
|
|
122
|
-
const projectName = options.project ?? defaultProjectName(workspace);
|
|
123
|
-
if (!projectName) {
|
|
124
|
-
throw new schematics_1.SchematicsException('Could not determine which project to set up. Re-run with --project=<name>.');
|
|
125
|
-
}
|
|
126
|
-
const project = workspace.projects.get(projectName);
|
|
127
|
-
if (!project) {
|
|
128
|
-
throw new schematics_1.SchematicsException(`Project "${projectName}" was not found in the workspace.`);
|
|
129
|
-
}
|
|
130
|
-
const build = project.targets.get('build');
|
|
131
|
-
if (!build) {
|
|
132
|
-
throw new schematics_1.SchematicsException(`Project "${projectName}" has no "build" target.`);
|
|
133
|
-
}
|
|
134
|
-
const buildOptions = (build.options ?? {});
|
|
135
|
-
const stylesheet = await resolveStylesheet(tree, context, projectName, project.sourceRoot, buildOptions);
|
|
136
|
-
return (0, schematics_1.chain)([
|
|
137
|
-
updateStylesheet(stylesheet, context),
|
|
138
|
-
options.fonts === false
|
|
139
|
-
? noop()
|
|
140
|
-
: updateIndexHtml(buildOptions.index, project.sourceRoot, context),
|
|
141
|
-
]);
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
function defaultProjectName(workspace) {
|
|
145
|
-
for (const [name, project] of workspace.projects) {
|
|
146
|
-
if (project.extensions['projectType'] === 'application') {
|
|
147
|
-
return name;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return undefined;
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Finds the app's global stylesheet, creating and registering one if the
|
|
154
|
-
* project has none.
|
|
155
|
-
*/
|
|
156
|
-
async function resolveStylesheet(tree, context, projectName, sourceRoot, buildOptions) {
|
|
157
|
-
const styles = buildOptions.styles ?? [];
|
|
158
|
-
const first = styles
|
|
159
|
-
.map((entry) => (typeof entry === 'string' ? entry : entry.input))
|
|
160
|
-
.find((path) => tree.exists(path));
|
|
161
|
-
if (first) {
|
|
162
|
-
if (!first.endsWith('.css')) {
|
|
163
|
-
context.logger.warn(` ! ${first} is not plain CSS. Tailwind v4 expects a .css entry point; ` +
|
|
164
|
-
`the directives were still added, but you may need to migrate this file.`);
|
|
165
|
-
}
|
|
166
|
-
return first;
|
|
167
|
-
}
|
|
168
|
-
const created = joinPath(sourceRoot ?? `projects/${projectName}/src`, 'styles.css');
|
|
169
|
-
tree.create(created, '');
|
|
170
|
-
await (0, utility_1.updateWorkspace)((workspace) => {
|
|
171
|
-
const target = workspace.projects.get(projectName)?.targets.get('build');
|
|
172
|
-
if (target) {
|
|
173
|
-
target.options ??= {};
|
|
174
|
-
const list = target.options['styles'] ?? [];
|
|
175
|
-
target.options['styles'] = [...list, created];
|
|
176
|
-
}
|
|
177
|
-
})(tree, context);
|
|
178
|
-
context.logger.info(` ✓ Created ${created} and registered it as a global stylesheet.`);
|
|
179
|
-
return created;
|
|
180
|
-
}
|
|
181
|
-
function updateStylesheet(path, context) {
|
|
182
|
-
return (tree) => {
|
|
183
|
-
const original = tree.readText(path);
|
|
184
|
-
// A v3 setup uses @tailwind directives. Adding a v4 `@import 'tailwindcss'`
|
|
185
|
-
// on top of that would produce a broken hybrid, so leave it be and say so.
|
|
186
|
-
const hasV3Directives = /^\s*@tailwind\s+(base|components|utilities)\s*;/m.test(original);
|
|
187
|
-
const hasV4Import = /@import\s+["']tailwindcss["']/.test(original);
|
|
188
|
-
if (hasV3Directives && !hasV4Import) {
|
|
189
|
-
context.logger.warn(` ! ${path} uses Tailwind v3 @tailwind directives. Skipping the Tailwind import — ` +
|
|
190
|
-
`migrate to v4 (@import 'tailwindcss') and re-run this schematic.`);
|
|
191
|
-
}
|
|
192
|
-
const wanted = [];
|
|
193
|
-
if (!hasV4Import && !hasV3Directives) {
|
|
194
|
-
wanted.push(TAILWIND_IMPORT);
|
|
195
|
-
}
|
|
196
|
-
if (!/@angular\/cdk\/overlay-prebuilt\.css/.test(original)) {
|
|
197
|
-
wanted.push(CDK_IMPORT);
|
|
198
|
-
}
|
|
199
|
-
if (!original.includes(`${PACKAGE_NAME}/styles/ui-kit.css`)) {
|
|
200
|
-
wanted.push(THEME_IMPORT);
|
|
201
|
-
}
|
|
202
|
-
const sourceDirective = buildSourceDirective(path);
|
|
203
|
-
const hasSource = new RegExp(`@source\\s+["'][^"']*${escapeRegExp(PACKAGE_NAME)}["']`).test(original);
|
|
204
|
-
if (!hasSource) {
|
|
205
|
-
wanted.push('', '/* Generates the kit\'s utility classes from its compiled templates.', ' * Tailwind skips node_modules by default, so this line is required. */', sourceDirective);
|
|
206
|
-
}
|
|
207
|
-
if (wanted.length === 0) {
|
|
208
|
-
context.logger.info(` ✓ ${path} is already configured.`);
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
const block = wanted.join('\n');
|
|
212
|
-
const eol = original.includes('\r\n') ? '\r\n' : '\n';
|
|
213
|
-
let updated;
|
|
214
|
-
if (hasV4Import || hasV3Directives) {
|
|
215
|
-
// Insert after the existing Tailwind entry so ordering stays correct.
|
|
216
|
-
const anchor = hasV4Import
|
|
217
|
-
? /@import\s+["']tailwindcss["'][^;]*;/
|
|
218
|
-
: /^\s*@tailwind\s+utilities\s*;/m;
|
|
219
|
-
const match = original.match(anchor);
|
|
220
|
-
if (match?.index !== undefined) {
|
|
221
|
-
const at = match.index + match[0].length;
|
|
222
|
-
updated = original.slice(0, at) + eol + block + original.slice(at);
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
updated = block + eol + original;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
updated = block + eol + (original.trimStart().length ? eol + original : '');
|
|
230
|
-
}
|
|
231
|
-
tree.overwrite(path, normaliseEol(updated, eol));
|
|
232
|
-
context.logger.info(` ✓ Wired ${path} (${wanted.filter(Boolean).length} directive(s) added).`);
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* `@source` paths resolve relative to the stylesheet that declares them, so
|
|
237
|
-
* this walks back up to the workspace root and down into node_modules.
|
|
238
|
-
*
|
|
239
|
-
* Deliberately plain string math: the devkit path helpers require absolute
|
|
240
|
-
* paths, and everything a schematic Tree hands back is workspace-relative.
|
|
241
|
-
*/
|
|
242
|
-
function buildSourceDirective(stylesheetPath) {
|
|
243
|
-
const depth = stylesheetPath.split('/').slice(0, -1).filter(Boolean).length;
|
|
244
|
-
const prefix = depth === 0 ? './' : '../'.repeat(depth);
|
|
245
|
-
return `@source '${prefix}node_modules/${PACKAGE_NAME}';`;
|
|
246
|
-
}
|
|
247
|
-
/** Workspace-relative path join that tolerates missing/trailing separators. */
|
|
248
|
-
function joinPath(...segments) {
|
|
249
|
-
return segments
|
|
250
|
-
.filter((segment) => !!segment)
|
|
251
|
-
.map((segment) => segment.replace(/^\/+/, '').replace(/\/+$/, ''))
|
|
252
|
-
.filter(Boolean)
|
|
253
|
-
.join('/');
|
|
254
|
-
}
|
|
255
|
-
/* -------------------------------------------------------------------------- *
|
|
256
|
-
* index.html
|
|
257
|
-
* -------------------------------------------------------------------------- */
|
|
258
|
-
function updateIndexHtml(index, sourceRoot, context) {
|
|
259
|
-
return (tree) => {
|
|
260
|
-
const path = (typeof index === 'string' ? index : index?.input) ??
|
|
261
|
-
joinPath(sourceRoot ?? 'src', 'index.html');
|
|
262
|
-
if (!tree.exists(path)) {
|
|
263
|
-
context.logger.warn(` ! ${path} not found; add the font <link> tags yourself.`);
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
const original = tree.readText(path);
|
|
267
|
-
if (original.includes('IBM+Plex+Sans')) {
|
|
268
|
-
context.logger.info(` ✓ ${path} already links IBM Plex Sans.`);
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
const eol = original.includes('\r\n') ? '\r\n' : '\n';
|
|
272
|
-
const links = [
|
|
273
|
-
` <link rel="preconnect" href="https://fonts.googleapis.com">`,
|
|
274
|
-
` <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>`,
|
|
275
|
-
` <link href="${FONT_HREF}" rel="stylesheet">`,
|
|
276
|
-
].join(eol);
|
|
277
|
-
const headClose = original.search(/<\/head>/i);
|
|
278
|
-
if (headClose === -1) {
|
|
279
|
-
context.logger.warn(` ! No </head> in ${path}; add the font <link> tags yourself.`);
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
const updated = original.slice(0, headClose) + links + eol + original.slice(headClose);
|
|
283
|
-
tree.overwrite(path, updated);
|
|
284
|
-
context.logger.info(` ✓ Linked IBM Plex Sans, IBM Plex Mono and Archivo in ${path}.`);
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
/* -------------------------------------------------------------------------- *
|
|
288
|
-
* Helpers
|
|
289
|
-
* -------------------------------------------------------------------------- */
|
|
290
|
-
function scheduleInstall(options) {
|
|
291
|
-
return (_tree, context) => {
|
|
292
|
-
if (!options.skipInstall) {
|
|
293
|
-
context.addTask(new tasks_1.NodePackageInstallTask());
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
function noop() {
|
|
298
|
-
return () => { };
|
|
299
|
-
}
|
|
300
|
-
function escapeRegExp(value) {
|
|
301
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
302
|
-
}
|
|
303
|
-
function normaliseEol(value, eol) {
|
|
304
|
-
return eol === '\r\n' ? value.replace(/\r?\n/g, '\r\n') : value;
|
|
305
|
-
}
|