create-middag-ui 0.11.0 → 0.13.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.
- package/README.md +1 -1
- package/cli.js +79 -16
- package/lib/detect.js +21 -1
- package/lib/scaffold.js +319 -58
- package/lib/scaffoldPRO.js +228 -0
- package/lib/templates/pro/app.tsx +50 -0
- package/lib/templates/pro/main.tsx +21 -0
- package/lib/templates/pro/mock-data.ts +28 -0
- package/lib/templates/pro/mock-entities.ts +13 -0
- package/lib/templates/pro/mock-navigation.ts +59 -0
- package/lib/templates/pro/mock-routes.tsx +90 -0
- package/lib/templates/pro/register-pro.ts +75 -0
- package/lib/templates/shared/demo-page.tsx +45 -0
- package/lib/templates/shared/moodle-ajax.ts +33 -0
- package/lib/templates/shared/moodle-notification.ts +23 -0
- package/lib/templates/shared/moodle-strings.ts +62 -0
- package/lib/templates/shared/page-resolver.tsx +51 -0
- package/lib/templates/shared/register-free.ts +64 -0
- package/lib/templates/shared/route-helper-custom.ts +44 -0
- package/lib/templates/shared/route-helper-moodle.ts +66 -0
- package/lib/templates/shared/route-helper-wp.ts +58 -0
- package/lib/templates/shared/tailwind.css +12 -0
- package/lib/templates/shared/vite-plugin-moodle-amd.ts +122 -0
- package/package.json +1 -1
package/lib/scaffold.js
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
11
|
-
import {
|
|
11
|
+
import { basename, dirname, join } from "node:path";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
|
-
import { success, warn
|
|
13
|
+
import { error, success, warn } from "./ui.js";
|
|
14
14
|
|
|
15
15
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
16
|
|
|
@@ -94,6 +94,13 @@ export function scaffoldPackageJson(targetDir, host, cwd, registryPath, hostKey)
|
|
|
94
94
|
deps["sonner"] = "^2.0.0";
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
// Moodle AMD build needs Tailwind Vite plugin for CSS processing
|
|
98
|
+
const moodleDevDeps = {};
|
|
99
|
+
if (hostKey === "moodle") {
|
|
100
|
+
moodleDevDeps["@tailwindcss/vite"] = "^4.0.0";
|
|
101
|
+
moodleDevDeps["tailwindcss"] = "^4.0.0";
|
|
102
|
+
}
|
|
103
|
+
|
|
97
104
|
const scripts = {
|
|
98
105
|
dev: "vite",
|
|
99
106
|
build: "vite build",
|
|
@@ -140,6 +147,7 @@ export function scaffoldPackageJson(targetDir, host, cwd, registryPath, hostKey)
|
|
|
140
147
|
eslint: "^9.0.0",
|
|
141
148
|
prettier: "^3.0.0",
|
|
142
149
|
"prettier-plugin-tailwindcss": "^0.6.0",
|
|
150
|
+
...moodleDevDeps,
|
|
143
151
|
},
|
|
144
152
|
};
|
|
145
153
|
|
|
@@ -166,7 +174,7 @@ export function scaffoldTsconfig(targetDir) {
|
|
|
166
174
|
paths: { "@/*": ["./src/*"] },
|
|
167
175
|
baseUrl: ".",
|
|
168
176
|
},
|
|
169
|
-
include: ["src"],
|
|
177
|
+
include: ["src", "mock"],
|
|
170
178
|
};
|
|
171
179
|
|
|
172
180
|
writeFile(filePath, JSON.stringify(tsconfig, null, 2) + "\n", "tsconfig.json");
|
|
@@ -312,6 +320,7 @@ export function scaffoldIndexHtml(targetDir) {
|
|
|
312
320
|
-->
|
|
313
321
|
<div id="root" class="middag-root"></div>
|
|
314
322
|
<div id="middag-portals" class="middag-root"></div>
|
|
323
|
+
<script>window.__MIDDAG_MOCK_NAVIGATE__ = true;</script>
|
|
315
324
|
<script type="module" src="/src/main.tsx"></script>
|
|
316
325
|
</body>
|
|
317
326
|
</html>
|
|
@@ -442,6 +451,22 @@ export type { PageContract, BlockDescriptor, SharedProps } from "@middag-io/reac
|
|
|
442
451
|
*
|
|
443
452
|
* Import order matters \u2014 this file is loaded AFTER @middag-io/react/style.css
|
|
444
453
|
* so overrides here take precedence.
|
|
454
|
+
*
|
|
455
|
+
* \u2500\u2500 Built-in themes (PRO) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
456
|
+
* PRO users: 4 built-in themes are imported in main.tsx:
|
|
457
|
+
* classic (Maia) \u2014 default, warm neutral, shadcn/ui feel
|
|
458
|
+
* enterprise \u2014 Jira/Linear, dense, corporate blue
|
|
459
|
+
* soft \u2014 Notion/Craft, friendly, rose/pink
|
|
460
|
+
* midnight \u2014 GitHub Dark/Vercel, dev-tools
|
|
461
|
+
*
|
|
462
|
+
* Switch via: document.body.classList.add("theme-enterprise")
|
|
463
|
+
* See: https://ui-docs.middag.io/guides/theme#built-in-themes
|
|
464
|
+
*
|
|
465
|
+
* \u2500\u2500 Custom themes (all users) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
466
|
+
* Override :root for global changes, or create a .theme-{name} class
|
|
467
|
+
* for a switchable theme. Both Community and PRO users can create
|
|
468
|
+
* unlimited custom themes.
|
|
469
|
+
* See: https://ui-docs.middag.io/guides/theme#custom-themes
|
|
445
470
|
*/
|
|
446
471
|
|
|
447
472
|
/* \u2500\u2500 Global token overrides \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
@@ -460,13 +485,13 @@ export type { PageContract, BlockDescriptor, SharedProps } from "@middag-io/reac
|
|
|
460
485
|
}
|
|
461
486
|
*/
|
|
462
487
|
|
|
463
|
-
/* \u2500\u2500 Example theme: Ocean \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
488
|
+
/* \u2500\u2500 Example custom theme: Ocean \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
464
489
|
*
|
|
465
|
-
* A complete theme
|
|
466
|
-
* Apply with:
|
|
490
|
+
* A complete custom theme scoped to a CSS class.
|
|
491
|
+
* Apply with: document.body.classList.add("theme-ocean")
|
|
467
492
|
*
|
|
468
|
-
*
|
|
469
|
-
*
|
|
493
|
+
* Override colors, radius, density, shadows, and layout in one class.
|
|
494
|
+
* Dark mode: use :root.dark .theme-ocean for dark overrides.
|
|
470
495
|
*/
|
|
471
496
|
|
|
472
497
|
.theme-ocean {
|
|
@@ -482,6 +507,13 @@ export type { PageContract, BlockDescriptor, SharedProps } from "@middag-io/reac
|
|
|
482
507
|
--info: oklch(0.55 0.14 200);
|
|
483
508
|
--info-foreground: oklch(0.98 0 0);
|
|
484
509
|
|
|
510
|
+
/* Shape \u2014 tighter corners, denser rows */
|
|
511
|
+
--radius: 0.5rem;
|
|
512
|
+
--radius-sm: 4px;
|
|
513
|
+
--radius-lg: 8px;
|
|
514
|
+
--size-table-row: 44px;
|
|
515
|
+
--size-button-md: 34px;
|
|
516
|
+
|
|
485
517
|
/* Sidebar */
|
|
486
518
|
--sidebar: oklch(0.15 0.03 230);
|
|
487
519
|
--sidebar-foreground: oklch(0.75 0.02 230);
|
|
@@ -493,6 +525,16 @@ export type { PageContract, BlockDescriptor, SharedProps } from "@middag-io/reac
|
|
|
493
525
|
--sidebar-border: oklch(0.25 0.03 230);
|
|
494
526
|
}
|
|
495
527
|
|
|
528
|
+
/* Dark mode for custom theme */
|
|
529
|
+
:root.dark .theme-ocean,
|
|
530
|
+
[data-theme="dark"] .theme-ocean {
|
|
531
|
+
--primary: oklch(0.65 0.14 230);
|
|
532
|
+
--primary-foreground: oklch(0.1 0.01 230);
|
|
533
|
+
--background: oklch(0.12 0.01 230);
|
|
534
|
+
--sidebar: oklch(0.10 0.02 230);
|
|
535
|
+
--sidebar-foreground: oklch(0.70 0.01 230);
|
|
536
|
+
}
|
|
537
|
+
|
|
496
538
|
/* \u2500\u2500 Custom project styles \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
497
539
|
*
|
|
498
540
|
* Add project-specific styles below. These can target MIDDAG components
|
|
@@ -506,17 +548,9 @@ export type { PageContract, BlockDescriptor, SharedProps } from "@middag-io/reac
|
|
|
506
548
|
* Radius: var(--radius-sm|md|lg|xl|2xl|full)
|
|
507
549
|
* Shadows: var(--shadow-xs|sm|md|lg|xl|2xl)
|
|
508
550
|
* Motion: var(--duration-fast|normal|moderate|slow)
|
|
551
|
+
* Sizing: var(--size-button-sm|md|lg), var(--size-table-row),
|
|
552
|
+
* var(--size-input), var(--sidebar-width)
|
|
509
553
|
*/
|
|
510
|
-
|
|
511
|
-
/* Example: branded page header */
|
|
512
|
-
/*
|
|
513
|
-
.my-page-header {
|
|
514
|
-
background: linear-gradient(135deg, var(--primary) 0%, var(--info) 100%);
|
|
515
|
-
color: var(--primary-foreground);
|
|
516
|
-
padding: var(--space-6) var(--space-8);
|
|
517
|
-
border-radius: var(--radius-lg);
|
|
518
|
-
}
|
|
519
|
-
*/
|
|
520
554
|
`,
|
|
521
555
|
"src/theme.css",
|
|
522
556
|
);
|
|
@@ -634,7 +668,7 @@ export const dashboardContract: PageContract = {
|
|
|
634
668
|
`/**
|
|
635
669
|
* Connectors page contract \u2014 INTERMEDIATE example.
|
|
636
670
|
*
|
|
637
|
-
* Demonstrates the "
|
|
671
|
+
* Demonstrates the "sidebar" layout with three block types:
|
|
638
672
|
* - card_grid (connector cards in the main region)
|
|
639
673
|
* - status_strip (health indicators in the aside)
|
|
640
674
|
* - detail_panel (metadata in the aside)
|
|
@@ -658,7 +692,7 @@ export const connectorsContract: PageContract = {
|
|
|
658
692
|
],
|
|
659
693
|
},
|
|
660
694
|
layout: {
|
|
661
|
-
template: "
|
|
695
|
+
template: "sidebar",
|
|
662
696
|
regions: {
|
|
663
697
|
main: [
|
|
664
698
|
{
|
|
@@ -1265,7 +1299,8 @@ export function scaffoldHostEntry(targetDir, hostKey) {
|
|
|
1265
1299
|
const label = `src/entry-${hostKey}.tsx`;
|
|
1266
1300
|
if (skipIfExists(filePath, label)) return;
|
|
1267
1301
|
|
|
1268
|
-
// Host-specific setup and post-mount code
|
|
1302
|
+
// Host-specific imports, setup, and post-mount code
|
|
1303
|
+
let extraImports = "";
|
|
1269
1304
|
let setupCode = "";
|
|
1270
1305
|
let postMountCode = "";
|
|
1271
1306
|
|
|
@@ -1290,7 +1325,20 @@ export function scaffoldHostEntry(targetDir, hostKey) {
|
|
|
1290
1325
|
});
|
|
1291
1326
|
observer.observe(document.body, { childList: true, subtree: true });`;
|
|
1292
1327
|
} else if (hostKey === "moodle") {
|
|
1293
|
-
|
|
1328
|
+
extraImports = `import "./tailwind.css";
|
|
1329
|
+
import "@fontsource-variable/figtree";`;
|
|
1330
|
+
setupCode = ` document.body.classList.add("middag-active");
|
|
1331
|
+
|
|
1332
|
+
// Portal container for Radix UI (modals, popovers, toasts).
|
|
1333
|
+
// Radix portals default to document.body which is outside .middag-root,
|
|
1334
|
+
// meaning scoped Tailwind styles won't apply. This creates a sibling
|
|
1335
|
+
// container with .middag-root so portal content inherits design tokens.
|
|
1336
|
+
if (!document.getElementById("middag-portals")) {
|
|
1337
|
+
const portalContainer = document.createElement("div");
|
|
1338
|
+
portalContainer.id = "middag-portals";
|
|
1339
|
+
portalContainer.classList.add("middag-root");
|
|
1340
|
+
document.body.appendChild(portalContainer);
|
|
1341
|
+
}`;
|
|
1294
1342
|
}
|
|
1295
1343
|
|
|
1296
1344
|
const content = `/**
|
|
@@ -1304,39 +1352,31 @@ export function scaffoldHostEntry(targetDir, hostKey) {
|
|
|
1304
1352
|
*/
|
|
1305
1353
|
import { createRoot } from "react-dom/client";
|
|
1306
1354
|
import { createInertiaApp } from "@inertiajs/react";
|
|
1307
|
-
import {
|
|
1308
|
-
registerDefaults,
|
|
1309
|
-
registerShell,
|
|
1310
|
-
ContractPage,
|
|
1311
|
-
HostProductShell,
|
|
1312
|
-
I18nProvider,
|
|
1313
|
-
ProgressProvider,
|
|
1314
|
-
ptBR,
|
|
1315
|
-
} from "@middag-io/react";
|
|
1316
|
-
import type { PageContract } from "@middag-io/react";
|
|
1355
|
+
import { I18nProvider, ProgressProvider } from "@middag-io/react";
|
|
1317
1356
|
import "@middag-io/react/style.css";
|
|
1318
1357
|
import "./theme.css";
|
|
1358
|
+
${ extraImports }import { registerDefaults } from "./app/register";
|
|
1359
|
+
import { resolvePageComponent } from "./app/page-resolver";
|
|
1319
1360
|
|
|
1320
1361
|
registerDefaults();
|
|
1321
|
-
registerShell("product", HostProductShell);
|
|
1322
1362
|
|
|
1323
1363
|
createInertiaApp({
|
|
1324
1364
|
id: "middag-app",
|
|
1325
|
-
resolve: () =>
|
|
1326
|
-
const Page = ({ contract }: { contract: PageContract }) => (
|
|
1327
|
-
<I18nProvider overrides={ptBR}>
|
|
1328
|
-
<ProgressProvider>
|
|
1329
|
-
<ContractPage contract={contract} />
|
|
1330
|
-
</ProgressProvider>
|
|
1331
|
-
</I18nProvider>
|
|
1332
|
-
);
|
|
1333
|
-
Page.displayName = "ContractPageWrapper";
|
|
1334
|
-
return Page;
|
|
1335
|
-
},
|
|
1365
|
+
resolve: (name) => resolvePageComponent(name),
|
|
1336
1366
|
setup({ el, App, props }) {
|
|
1337
1367
|
el.classList.add("middag-root");
|
|
1338
1368
|
${setupCode}
|
|
1339
|
-
createRoot(el).render(
|
|
1369
|
+
createRoot(el).render(
|
|
1370
|
+
<ProgressProvider>
|
|
1371
|
+
<App {...props}>
|
|
1372
|
+
{({ Component, props: pageProps, key }) => (
|
|
1373
|
+
<I18nProvider>
|
|
1374
|
+
<Component key={key} {...pageProps} />
|
|
1375
|
+
</I18nProvider>
|
|
1376
|
+
)}
|
|
1377
|
+
</App>
|
|
1378
|
+
</ProgressProvider>,
|
|
1379
|
+
);
|
|
1340
1380
|
${postMountCode}
|
|
1341
1381
|
},
|
|
1342
1382
|
});
|
|
@@ -1372,17 +1412,71 @@ export function scaffoldHostViteConfig(targetDir, hostKey, host) {
|
|
|
1372
1412
|
},
|
|
1373
1413
|
},`;
|
|
1374
1414
|
} else if (hostKey === "moodle") {
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1415
|
+
// Moodle uses AMD format with vite-plugin-moodle-amd.
|
|
1416
|
+
// Generate a dedicated AMD config instead of the shared lib template.
|
|
1417
|
+
const content = `/**
|
|
1418
|
+
* Vite build config for Moodle — AMD production build.
|
|
1419
|
+
*
|
|
1420
|
+
* Usage:
|
|
1421
|
+
* npm run build:moodle \u2192 AMD chunks to dist/ + amd/
|
|
1422
|
+
* npm run watch:moodle \u2192 rebuild on change
|
|
1423
|
+
*
|
|
1424
|
+
* Output: AMD modules compatible with Moodle's RequireJS loader.
|
|
1425
|
+
* vite-plugin-moodle-amd rewrites chunk paths and copies to amd/.
|
|
1426
|
+
* CSS is copied to styles/middag-app.css (Moodle auto-discovers it).
|
|
1427
|
+
*
|
|
1428
|
+
* The dev server (\`npm run dev\`) uses vite.config.ts instead.
|
|
1429
|
+
*/
|
|
1430
|
+
import { defineConfig } from "vite";
|
|
1431
|
+
import react from "@vitejs/plugin-react";
|
|
1432
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
1433
|
+
import moodleAmd from "./plugins/vite-plugin-moodle-amd";
|
|
1434
|
+
import { resolve } from "path";
|
|
1435
|
+
|
|
1436
|
+
export default defineConfig({
|
|
1437
|
+
plugins: [react(), tailwindcss(), moodleAmd()],
|
|
1438
|
+
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
1439
|
+
resolve: { alias: { "@/": resolve(__dirname, "src") + "/" } },
|
|
1440
|
+
build: {
|
|
1441
|
+
outDir: resolve(__dirname, "dist"),
|
|
1442
|
+
emptyOutDir: true,
|
|
1443
|
+
minify: "esbuild",
|
|
1444
|
+
cssCodeSplit: false,
|
|
1445
|
+
rollupOptions: {
|
|
1446
|
+
input: { "middag-app": resolve(__dirname, "src/entry-moodle.tsx") },
|
|
1447
|
+
external: ["core/ajax", "core/str", "core/notification", "jquery"],
|
|
1380
1448
|
output: {
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1449
|
+
format: "amd",
|
|
1450
|
+
dir: resolve(__dirname, "dist"),
|
|
1451
|
+
entryFileNames: "[name].js",
|
|
1452
|
+
chunkFileNames: "[name].js",
|
|
1453
|
+
manualChunks(id) {
|
|
1454
|
+
if (
|
|
1455
|
+
id.includes("node_modules/react/") ||
|
|
1456
|
+
id.includes("node_modules/react-dom/") ||
|
|
1457
|
+
id.includes("node_modules/scheduler/") ||
|
|
1458
|
+
id.includes("node_modules/@inertiajs/")
|
|
1459
|
+
) {
|
|
1460
|
+
return "react-vendor-lazy";
|
|
1461
|
+
}
|
|
1462
|
+
if (
|
|
1463
|
+
id.includes("node_modules/@radix-ui/") ||
|
|
1464
|
+
id.includes("node_modules/@base-ui/") ||
|
|
1465
|
+
id.includes("node_modules/class-variance-authority/")
|
|
1466
|
+
) {
|
|
1467
|
+
return "react-ui-lazy";
|
|
1468
|
+
}
|
|
1469
|
+
if (id.includes("node_modules/@tanstack/")) {
|
|
1470
|
+
return "react-table-lazy";
|
|
1471
|
+
}
|
|
1384
1472
|
},
|
|
1385
|
-
}
|
|
1473
|
+
},
|
|
1474
|
+
},
|
|
1475
|
+
},
|
|
1476
|
+
});
|
|
1477
|
+
`;
|
|
1478
|
+
writeFile(filePath, content, label);
|
|
1479
|
+
return;
|
|
1386
1480
|
} else {
|
|
1387
1481
|
outDir = `resolve(__dirname, "../dist")`;
|
|
1388
1482
|
formats = `["es"]`;
|
|
@@ -1445,7 +1539,7 @@ export default defineConfig({
|
|
|
1445
1539
|
export function scaffoldHostThemeCSS(targetDir, hostKey, host) {
|
|
1446
1540
|
const themePath = join(targetDir, "src", "theme.css");
|
|
1447
1541
|
|
|
1448
|
-
let hostSection
|
|
1542
|
+
let hostSection;
|
|
1449
1543
|
|
|
1450
1544
|
if (hostKey === "wordpress") {
|
|
1451
1545
|
hostSection = `
|
|
@@ -1505,8 +1599,18 @@ body.middag-active {
|
|
|
1505
1599
|
} else if (hostKey === "moodle") {
|
|
1506
1600
|
hostSection = `
|
|
1507
1601
|
|
|
1508
|
-
/*
|
|
1509
|
-
* Active when MIDDAG mounts inside Moodle
|
|
1602
|
+
/* ── Moodle Boost integration ────────────────────────────────────────────
|
|
1603
|
+
* Active when MIDDAG mounts inside Moodle (body.middag-active).
|
|
1604
|
+
*
|
|
1605
|
+
* CSS Isolation Strategy:
|
|
1606
|
+
*
|
|
1607
|
+
* OUTWARD (MIDDAG → Moodle):
|
|
1608
|
+
* Tailwind utility classes only match elements that carry them.
|
|
1609
|
+
* Known collisions with Bootstrap are neutralized below.
|
|
1610
|
+
*
|
|
1611
|
+
* INWARD (Moodle → MIDDAG):
|
|
1612
|
+
* .middag-root blocks inherited Moodle/Bootstrap styles.
|
|
1613
|
+
* Portals render inside #middag-portals (also .middag-root).
|
|
1510
1614
|
*/
|
|
1511
1615
|
|
|
1512
1616
|
body.middag-active {
|
|
@@ -1515,7 +1619,31 @@ body.middag-active {
|
|
|
1515
1619
|
|
|
1516
1620
|
body.middag-active [data-slot="sidebar-container"] {
|
|
1517
1621
|
left: 0 !important;
|
|
1518
|
-
}
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
/* ── Outward isolation: Tailwind ↔ Bootstrap collision fixes ─────────────
|
|
1625
|
+
*
|
|
1626
|
+
* .collapse — Bootstrap uses display:none/block for collapsible fieldsets.
|
|
1627
|
+
* Tailwind emits \`.collapse { visibility: collapse }\` which breaks them.
|
|
1628
|
+
* Neutralize outside the React tree (.middag-root).
|
|
1629
|
+
*
|
|
1630
|
+
* Add new entries here as collisions are discovered.
|
|
1631
|
+
*/
|
|
1632
|
+
.collapse:not(.middag-root .collapse, .middag-root.collapse) {
|
|
1633
|
+
visibility: visible !important;
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
/* ── Theme bridge: inherit Moodle Boost primary color ────────────────────
|
|
1637
|
+
*
|
|
1638
|
+
* If Boost defines --bs-primary, map it to --middag-brand so the
|
|
1639
|
+
* MIDDAG UI inherits the institution's brand color automatically.
|
|
1640
|
+
* Uncomment and adjust if your Moodle theme uses a different variable.
|
|
1641
|
+
*/
|
|
1642
|
+
/*
|
|
1643
|
+
:root {
|
|
1644
|
+
--middag-brand: var(--bs-primary);
|
|
1645
|
+
}
|
|
1646
|
+
*/`;
|
|
1519
1647
|
} else {
|
|
1520
1648
|
hostSection = `
|
|
1521
1649
|
|
|
@@ -1787,3 +1915,136 @@ export const router = {
|
|
|
1787
1915
|
);
|
|
1788
1916
|
}
|
|
1789
1917
|
}
|
|
1918
|
+
|
|
1919
|
+
// ── Template reader ─────────────────────────────────────────────────────
|
|
1920
|
+
|
|
1921
|
+
/** Read a template file relative to this script's directory. */
|
|
1922
|
+
function readTemplate(relativePath) {
|
|
1923
|
+
return readFileSync(join(__dirname, relativePath), "utf-8");
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
// ── Shared: register, page-resolver, route helper, demo page ────────────
|
|
1927
|
+
|
|
1928
|
+
/**
|
|
1929
|
+
* Scaffold FREE register: src/app/register.ts — minimal (5 blocks).
|
|
1930
|
+
*/
|
|
1931
|
+
export function scaffoldFreeRegister(targetDir) {
|
|
1932
|
+
ensureDir(join(targetDir, "src", "app"));
|
|
1933
|
+
const filePath = join(targetDir, "src", "app", "register.ts");
|
|
1934
|
+
if (skipIfExists(filePath, "src/app/register.ts")) return;
|
|
1935
|
+
writeFile(filePath, readTemplate("templates/shared/register-free.ts"), "src/app/register.ts (FREE)");
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
/**
|
|
1939
|
+
* Scaffold page resolver: src/app/page-resolver.tsx.
|
|
1940
|
+
* Supports Contract: prefix pages (ContractPage) and direct pages (glob).
|
|
1941
|
+
*/
|
|
1942
|
+
export function scaffoldPageResolver(targetDir) {
|
|
1943
|
+
ensureDir(join(targetDir, "src", "app"));
|
|
1944
|
+
const filePath = join(targetDir, "src", "app", "page-resolver.tsx");
|
|
1945
|
+
if (skipIfExists(filePath, "src/app/page-resolver.tsx")) return;
|
|
1946
|
+
writeFile(filePath, readTemplate("templates/shared/page-resolver.tsx"), "src/app/page-resolver.tsx");
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
/**
|
|
1950
|
+
* Scaffold route helper: src/lib/routes.ts.
|
|
1951
|
+
* Abstracts host admin URL vs dev mock path.
|
|
1952
|
+
*
|
|
1953
|
+
* @param {string} hostKey - 'wordpress' | 'moodle' | 'custom'
|
|
1954
|
+
*/
|
|
1955
|
+
/**
|
|
1956
|
+
* @param {string} targetDir
|
|
1957
|
+
* @param {string} hostKey - 'wordpress' | 'moodle' | 'custom'
|
|
1958
|
+
* @param {string} [pluginSlug] - For Moodle: slug portion of frankenstyle (e.g. "middag" from "local_middag")
|
|
1959
|
+
*/
|
|
1960
|
+
export function scaffoldRouteHelper(targetDir, hostKey, pluginSlug) {
|
|
1961
|
+
ensureDir(join(targetDir, "src", "lib"));
|
|
1962
|
+
const filePath = join(targetDir, "src", "lib", "routes.ts");
|
|
1963
|
+
if (skipIfExists(filePath, "src/lib/routes.ts")) return;
|
|
1964
|
+
|
|
1965
|
+
const templateMap = {
|
|
1966
|
+
wordpress: "templates/shared/route-helper-wp.ts",
|
|
1967
|
+
moodle: "templates/shared/route-helper-moodle.ts",
|
|
1968
|
+
custom: "templates/shared/route-helper-custom.ts",
|
|
1969
|
+
};
|
|
1970
|
+
const template = templateMap[hostKey] || templateMap.wordpress;
|
|
1971
|
+
|
|
1972
|
+
try {
|
|
1973
|
+
let content = readTemplate(template);
|
|
1974
|
+
if (hostKey === "moodle" && pluginSlug) {
|
|
1975
|
+
// Convert frankenstyle slug to Moodle path: "middag" with type prefix "local" → "local/middag"
|
|
1976
|
+
// pluginSlug is the name part; the full frankenstyle comes from cli.js
|
|
1977
|
+
content = content.replace(/__PLUGIN_PATH__/g, pluginSlug);
|
|
1978
|
+
}
|
|
1979
|
+
writeFile(filePath, content, "src/lib/routes.ts");
|
|
1980
|
+
} catch {
|
|
1981
|
+
// Fallback to WP template if host-specific one doesn't exist
|
|
1982
|
+
writeFile(filePath, readTemplate("templates/shared/route-helper-wp.ts"), "src/lib/routes.ts");
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
/**
|
|
1987
|
+
* Scaffold demo direct page: src/pages/DemoPage.tsx.
|
|
1988
|
+
* Shows the direct page pattern (usePage + custom React).
|
|
1989
|
+
*/
|
|
1990
|
+
export function scaffoldDemoDirectPage(targetDir) {
|
|
1991
|
+
ensureDir(join(targetDir, "src", "pages"));
|
|
1992
|
+
const filePath = join(targetDir, "src", "pages", "DemoPage.tsx");
|
|
1993
|
+
if (skipIfExists(filePath, "src/pages/DemoPage.tsx")) return;
|
|
1994
|
+
writeFile(filePath, readTemplate("templates/shared/demo-page.tsx"), "src/pages/DemoPage.tsx");
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
// ── Moodle-specific scaffold functions ────────────────────────────────────
|
|
1998
|
+
|
|
1999
|
+
/**
|
|
2000
|
+
* Scaffold vite-plugin-moodle-amd: plugins/vite-plugin-moodle-amd.ts.
|
|
2001
|
+
* Converts Rollup AMD output to Moodle's RequireJS format and copies
|
|
2002
|
+
* built files to amd/src/ + amd/build/ + styles/.
|
|
2003
|
+
*
|
|
2004
|
+
* @param {string} targetDir - Absolute path to UI dir
|
|
2005
|
+
* @param {string} pluginPrefix - Moodle frankenstyle (e.g. "local_middag")
|
|
2006
|
+
*/
|
|
2007
|
+
export function scaffoldMoodlePlugin(targetDir, pluginPrefix) {
|
|
2008
|
+
ensureDir(join(targetDir, "plugins"));
|
|
2009
|
+
const filePath = join(targetDir, "plugins", "vite-plugin-moodle-amd.ts");
|
|
2010
|
+
if (skipIfExists(filePath, "plugins/vite-plugin-moodle-amd.ts")) return;
|
|
2011
|
+
const content = readTemplate("templates/shared/vite-plugin-moodle-amd.ts")
|
|
2012
|
+
.replace(/__PLUGIN_PREFIX__/g, pluginPrefix);
|
|
2013
|
+
writeFile(filePath, content, "plugins/vite-plugin-moodle-amd.ts");
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
/**
|
|
2017
|
+
* Scaffold Tailwind CSS entry for Moodle AMD build: src/tailwind.css.
|
|
2018
|
+
* Brings in the Tailwind engine so local pages get their utilities compiled.
|
|
2019
|
+
*/
|
|
2020
|
+
export function scaffoldMoodleTailwind(targetDir) {
|
|
2021
|
+
ensureDir(join(targetDir, "src"));
|
|
2022
|
+
const filePath = join(targetDir, "src", "tailwind.css");
|
|
2023
|
+
if (skipIfExists(filePath, "src/tailwind.css")) return;
|
|
2024
|
+
writeFile(filePath, readTemplate("templates/shared/tailwind.css"), "src/tailwind.css");
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
/**
|
|
2028
|
+
* Scaffold Moodle AMD adapters: src/lib/moodle/.
|
|
2029
|
+
* Typed wrappers for core/ajax, core/str, core/notification.
|
|
2030
|
+
*
|
|
2031
|
+
* @param {string} targetDir - Absolute path to UI dir
|
|
2032
|
+
* @param {string} pluginPrefix - Moodle frankenstyle (e.g. "local_middag")
|
|
2033
|
+
*/
|
|
2034
|
+
export function scaffoldMoodleAdapters(targetDir, pluginPrefix) {
|
|
2035
|
+
ensureDir(join(targetDir, "src", "lib", "moodle"));
|
|
2036
|
+
|
|
2037
|
+
const files = [
|
|
2038
|
+
{ template: "templates/shared/moodle-ajax.ts", dest: "src/lib/moodle/ajax.ts" },
|
|
2039
|
+
{ template: "templates/shared/moodle-strings.ts", dest: "src/lib/moodle/strings.ts" },
|
|
2040
|
+
{ template: "templates/shared/moodle-notification.ts", dest: "src/lib/moodle/notification.ts" },
|
|
2041
|
+
];
|
|
2042
|
+
|
|
2043
|
+
for (const { template, dest } of files) {
|
|
2044
|
+
const filePath = join(targetDir, dest);
|
|
2045
|
+
if (skipIfExists(filePath, dest)) continue;
|
|
2046
|
+
const content = readTemplate(template)
|
|
2047
|
+
.replace(/__PLUGIN_PREFIX__/g, pluginPrefix);
|
|
2048
|
+
writeFile(filePath, content, dest);
|
|
2049
|
+
}
|
|
2050
|
+
}
|