create-bluecopa-react-app 1.0.39 → 1.0.41
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 +9 -9
- package/package.json +1 -1
- package/templates/latest/Agent.md +23 -23
- package/templates/latest/README.md +6 -6
- package/templates/latest/app/routes/statements.tsx +1 -1
- package/templates/latest/app/single-spa.tsx +102 -3
- package/templates/latest/dist/assets/{__federation_expose_App-OFfdinOR.js → __federation_expose_App-D-lv9y21.js} +68 -4
- package/templates/latest/dist/assets/{client-CkHcT_xc.js → client-Dms8K6Dw.js} +8840 -5996
- package/templates/latest/dist/assets/{index-B3cD3sP_.js → index-BrhXrqF7.js} +1 -1
- package/templates/latest/dist/assets/remoteEntry.js +1 -1
- package/templates/latest/dist/index.html +2 -2
- package/templates/latest/package-lock.json +31 -15
- package/templates/latest/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# create-bluecopa-react-app [](https://www.npmjs.com/package/create-bluecopa-react-app) [](LICENSE)
|
|
2
2
|
|
|
3
|
-
## A CLI Tool for
|
|
3
|
+
## A CLI Tool for Bluecopa React Applications
|
|
4
4
|
|
|
5
|
-
A command-line interface for bootstrapping modern React applications with
|
|
5
|
+
A command-line interface for bootstrapping modern React applications with Bluecopa integration, featuring opinionated best practices and pre-configured tooling.
|
|
6
6
|
|
|
7
7
|
## Table of Contents
|
|
8
8
|
|
|
@@ -50,9 +50,9 @@ export BLUECOPA_BOILERPLATE_REGISTRY=https://registry.npmjs.org
|
|
|
50
50
|
- 🌐 **Microfrontend Support**: Single-spa compatible with module federation
|
|
51
51
|
- 📊 **Data Visualization**: Recharts integration for charts and analytics
|
|
52
52
|
- 🎨 **UI Components**: shadcn/ui components built on Radix UI primitives
|
|
53
|
-
- 📡 **
|
|
53
|
+
- 📡 **Bluecopa React Components**: Pre-configured [`@bluecopa/react`](packages/react:1) package with hooks (includes TanStack Query integration)
|
|
54
54
|
- 🎯 **Type Safety**: Full TypeScript support
|
|
55
|
-
- 📱 **Responsive Design**: Tailwind CSS v4 with mobile-first approach and
|
|
55
|
+
- 📱 **Responsive Design**: Tailwind CSS v4 with mobile-first approach and Bluecopa design system
|
|
56
56
|
- 🛠️ **Development Tools**: ESLint, TypeScript checking, and Vite fast build system
|
|
57
57
|
- ⚡ **Fast Development**: Hot module replacement with Vite
|
|
58
58
|
- 🔄 **Server-side Rendering**: React Router v7 with SSR support
|
|
@@ -93,7 +93,7 @@ npx create-bluecopa-react-app my-dashboard --template shadcn
|
|
|
93
93
|
|
|
94
94
|
### `create-bluecopa-react-app [project-name] [options]`
|
|
95
95
|
|
|
96
|
-
Creates a new
|
|
96
|
+
Creates a new Bluecopa React application.
|
|
97
97
|
|
|
98
98
|
**Parameters:**
|
|
99
99
|
- `project-name`: Name of the new project directory
|
|
@@ -143,21 +143,21 @@ npx create-bluecopa-react-app my-dashboard
|
|
|
143
143
|
- **React Router v7** - Modern routing with SSR support
|
|
144
144
|
- **Vite 6** - Fast build tool and development server
|
|
145
145
|
- **TypeScript** - Full type safety and developer experience
|
|
146
|
-
- **[`@bluecopa/react`](packages/react:1)** -
|
|
146
|
+
- **[`@bluecopa/react`](packages/react:1)** - Bluecopa-specific React components and hooks
|
|
147
147
|
- **shadcn/ui** - Accessible component library built on Radix UI
|
|
148
148
|
- **Radix UI** - Unstyled, accessible UI primitives
|
|
149
149
|
- **TanStack Table** - Headless table library for React
|
|
150
150
|
- **Recharts** - Composable charting library for React
|
|
151
151
|
- **DND Kit** - Drag and drop toolkit for React
|
|
152
|
-
- **Tailwind CSS v4** - Utility-first CSS framework with
|
|
152
|
+
- **Tailwind CSS v4** - Utility-first CSS framework with Bluecopa design system
|
|
153
153
|
- **next-themes** - Theme switching with dark mode support
|
|
154
154
|
- **Lucide React** - Beautiful, customizable icons
|
|
155
155
|
|
|
156
156
|
## Monorepo Integration
|
|
157
157
|
|
|
158
|
-
This CLI tool is part of the
|
|
158
|
+
This CLI tool is part of the Bluecopa UI monorepo and integrates with the following packages:
|
|
159
159
|
|
|
160
|
-
- **[`@bluecopa/react`](packages/react:1)** -
|
|
160
|
+
- **[`@bluecopa/react`](packages/react:1)** - Bluecopa-specific React components and hooks
|
|
161
161
|
|
|
162
162
|
### Standalone Development
|
|
163
163
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# AI Agent Guide for
|
|
1
|
+
# AI Agent Guide for Bluecopa React UI Template
|
|
2
2
|
|
|
3
|
-
This guide helps AI assistants and developers understand the project structure, patterns, and best practices for working with this
|
|
3
|
+
This guide helps AI assistants and developers understand the project structure, patterns, and best practices for working with this Bluecopa React application template.
|
|
4
4
|
|
|
5
5
|
## 🏗️ Project Architecture Overview
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ This guide helps AI assistants and developers understand the project structure,
|
|
|
8
8
|
- **React 18** with TypeScript
|
|
9
9
|
- **React Router v7** with SSR support
|
|
10
10
|
- **shadcn/ui** components built on Radix UI
|
|
11
|
-
- **
|
|
11
|
+
- **Bluecopa UI** components from `@bluecopa-ui` registry (built on Radix UI)
|
|
12
12
|
- **Tailwind CSS v4** with CSS variables
|
|
13
13
|
- **Vite 6** for build tooling
|
|
14
14
|
- **TanStack Query** (via @bluecopa/react)
|
|
@@ -17,7 +17,7 @@ This guide helps AI assistants and developers understand the project structure,
|
|
|
17
17
|
```
|
|
18
18
|
app/
|
|
19
19
|
├── components/ # Reusable UI components
|
|
20
|
-
│ ├── ui/ #
|
|
20
|
+
│ ├── ui/ # Bluecopa UI or shadcn/ui components
|
|
21
21
|
│ ├── app-sidebar.tsx # Main navigation
|
|
22
22
|
│ ├── data-table.tsx # TanStack Table wrapper
|
|
23
23
|
│ └── chart-*.tsx # Recharts components
|
|
@@ -33,10 +33,10 @@ app/
|
|
|
33
33
|
|
|
34
34
|
This project supports components from two registries:
|
|
35
35
|
|
|
36
|
-
1. **
|
|
36
|
+
1. **Bluecopa UI Registry** (Preferred for Bluecopa-specific components)
|
|
37
37
|
- Registry URL: https://blui.vercel.app
|
|
38
38
|
- Registry JSON: https://blui.vercel.app/r/registry.json
|
|
39
|
-
- Custom
|
|
39
|
+
- Custom Bluecopa components built on Radix UI
|
|
40
40
|
|
|
41
41
|
2. **shadcn/ui Registry** (Standard components)
|
|
42
42
|
- Registry URL: https://ui.shadcn.com
|
|
@@ -51,9 +51,9 @@ Both registries:
|
|
|
51
51
|
|
|
52
52
|
### Adding Components
|
|
53
53
|
|
|
54
|
-
#### From
|
|
54
|
+
#### From Bluecopa UI Registry
|
|
55
55
|
|
|
56
|
-
To add a component from the
|
|
56
|
+
To add a component from the Bluecopa UI registry (preferred for Bluecopa-specific components):
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
59
|
pnpm dlx shadcn@latest add @bluecopa-ui/button
|
|
@@ -80,7 +80,7 @@ The registries are configured in `components.json`:
|
|
|
80
80
|
}
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
**Note**: When no registry prefix is specified, components are added from the default shadcn/ui registry. Use `@bluecopa-ui/` prefix for
|
|
83
|
+
**Note**: When no registry prefix is specified, components are added from the default shadcn/ui registry. Use `@bluecopa-ui/` prefix for Bluecopa-specific components.
|
|
84
84
|
|
|
85
85
|
### Custom Components
|
|
86
86
|
- Use TypeScript interfaces for props
|
|
@@ -128,14 +128,14 @@ A React library providing opinionated custom hooks for TanStack React Query inte
|
|
|
128
128
|
|
|
129
129
|
## Table of Contents
|
|
130
130
|
|
|
131
|
-
- [AI Agent Guide for
|
|
131
|
+
- [AI Agent Guide for Bluecopa React UI Template](#ai-agent-guide-for-bluecopa-react-ui-template)
|
|
132
132
|
- [🏗️ Project Architecture Overview](#️-project-architecture-overview)
|
|
133
133
|
- [Core Stack](#core-stack)
|
|
134
134
|
- [Key Directories](#key-directories)
|
|
135
135
|
- [🎨 Component Patterns](#-component-patterns)
|
|
136
136
|
- [UI Component Registries](#ui-component-registries)
|
|
137
137
|
- [Adding Components](#adding-components)
|
|
138
|
-
- [From
|
|
138
|
+
- [From Bluecopa UI Registry](#from-bluecopa-ui-registry)
|
|
139
139
|
- [From shadcn/ui Registry](#from-shadcnui-registry)
|
|
140
140
|
- [Custom Components](#custom-components)
|
|
141
141
|
- [Example Component Structure](#example-component-structure)
|
|
@@ -211,7 +211,7 @@ A React library providing opinionated custom hooks for TanStack React Query inte
|
|
|
211
211
|
- [Development Tools](#development-tools)
|
|
212
212
|
- [📚 Useful Resources](#-useful-resources)
|
|
213
213
|
- [Documentation](#documentation)
|
|
214
|
-
- [
|
|
214
|
+
- [Bluecopa Specific](#bluecopa-specific)
|
|
215
215
|
- [🎯 Best Practices Summary](#-best-practices-summary)
|
|
216
216
|
- [🔄 Common Workflows](#-common-workflows)
|
|
217
217
|
- [Adding a New Feature](#adding-a-new-feature)
|
|
@@ -775,20 +775,20 @@ import {
|
|
|
775
775
|
|
|
776
776
|
### Adding New Components
|
|
777
777
|
1. Add components from either registry:
|
|
778
|
-
-
|
|
778
|
+
- Bluecopa UI: `pnpm dlx shadcn@latest add @bluecopa-ui/component-name`
|
|
779
779
|
- shadcn/ui: `pnpm dlx shadcn@latest add component-name`
|
|
780
780
|
2. Components are installed in `app/components/ui/`
|
|
781
|
-
3. Prefer
|
|
782
|
-
4. Use shadcn/ui registry for standard components not available in
|
|
781
|
+
3. Prefer Bluecopa UI registry for Bluecopa-specific components
|
|
782
|
+
4. Use shadcn/ui registry for standard components not available in Bluecopa UI
|
|
783
783
|
5. Define TypeScript interface for props if extending components
|
|
784
784
|
6. Implement responsive design
|
|
785
785
|
7. Add proper accessibility attributes
|
|
786
786
|
|
|
787
787
|
### Working with Forms
|
|
788
788
|
1. Use form components from either registry:
|
|
789
|
-
-
|
|
789
|
+
- Bluecopa UI: `pnpm dlx shadcn@latest add @bluecopa-ui/form`
|
|
790
790
|
- shadcn/ui: `pnpm dlx shadcn@latest add form`
|
|
791
|
-
2. Prefer
|
|
791
|
+
2. Prefer Bluecopa UI form components when available
|
|
792
792
|
3. Implement validation with Zod
|
|
793
793
|
4. Handle form state with React hooks
|
|
794
794
|
5. Show loading and error states
|
|
@@ -886,21 +886,21 @@ import {
|
|
|
886
886
|
|
|
887
887
|
### Documentation
|
|
888
888
|
- [React Router v7 Docs](https://reactrouter.com/)
|
|
889
|
-
- [
|
|
889
|
+
- [Bluecopa UI Registry](https://blui.vercel.app)
|
|
890
890
|
- [shadcn/ui Components](https://ui.shadcn.com/)
|
|
891
891
|
- [Tailwind CSS v4](https://tailwindcss.com/)
|
|
892
892
|
- [TanStack Table](https://tanstack.com/table)
|
|
893
893
|
- [Recharts](https://recharts.org/)
|
|
894
894
|
|
|
895
|
-
###
|
|
895
|
+
### Bluecopa Specific
|
|
896
896
|
- [@bluecopa/react Package](packages/react:1)
|
|
897
|
-
-
|
|
897
|
+
- Bluecopa Design System
|
|
898
898
|
- API Integration Patterns
|
|
899
899
|
|
|
900
900
|
## 🎯 Best Practices Summary
|
|
901
901
|
|
|
902
902
|
1. **Always use TypeScript** - Define proper interfaces and types
|
|
903
|
-
2. **Use appropriate registry** - Prefer
|
|
903
|
+
2. **Use appropriate registry** - Prefer Bluecopa UI (`@bluecopa-ui/component-name`) for Bluecopa-specific components, use shadcn/ui (`component-name`) for standard components
|
|
904
904
|
3. **Follow component patterns** - Use established component structure from both registries
|
|
905
905
|
4. **Implement accessibility** - Use semantic HTML and ARIA attributes
|
|
906
906
|
5. **Optimize for performance** - Use proper React patterns and code splitting
|
|
@@ -916,7 +916,7 @@ import {
|
|
|
916
916
|
### Adding a New Feature
|
|
917
917
|
1. Create feature branch
|
|
918
918
|
2. Add required UI components from appropriate registry:
|
|
919
|
-
-
|
|
919
|
+
- Bluecopa UI: `pnpm dlx shadcn@latest add @bluecopa-ui/component-name`
|
|
920
920
|
- shadcn/ui: `pnpm dlx shadcn@latest add component-name`
|
|
921
921
|
3. Implement components with TypeScript
|
|
922
922
|
4. Add proper styling with Tailwind
|
|
@@ -933,4 +933,4 @@ import {
|
|
|
933
933
|
5. Verify responsive behavior
|
|
934
934
|
6. Test accessibility features
|
|
935
935
|
|
|
936
|
-
This guide should help AI assistants and developers work effectively with this
|
|
936
|
+
This guide should help AI assistants and developers work effectively with this Bluecopa React template, ensuring consistent patterns and best practices are followed throughout development.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Bluecopa React App with shadcn/ui
|
|
2
2
|
|
|
3
3
|
A modern, production-ready template for building full-stack React applications using React Router v7 and shadcn/ui components.
|
|
4
4
|
|
|
@@ -17,7 +17,7 @@ A modern, production-ready template for building full-stack React applications u
|
|
|
17
17
|
- 🎯 **Drag & Drop** - DND Kit integration for interactive interfaces
|
|
18
18
|
- 📱 **Responsive Design** - Mobile-first approach with sidebar navigation
|
|
19
19
|
- 🌙 **Dark Mode** - Built-in theme switching with next-themes
|
|
20
|
-
- 🔗 **
|
|
20
|
+
- 🔗 **Bluecopa API integration** - Pre-configured with `@bluecopa/react` hooks
|
|
21
21
|
- 📖 [React Router docs](https://reactrouter.com/)
|
|
22
22
|
- 🎨 [shadcn/ui docs](https://ui.shadcn.com/)
|
|
23
23
|
|
|
@@ -37,11 +37,11 @@ pnpm install
|
|
|
37
37
|
|
|
38
38
|
## Quick Start
|
|
39
39
|
|
|
40
|
-
This template is designed to work both as a standalone application and as a microfrontend within the
|
|
40
|
+
This template is designed to work both as a standalone application and as a microfrontend within the Bluecopa ecosystem. It includes:
|
|
41
41
|
|
|
42
42
|
- **Single-spa integration** for microfrontend architecture
|
|
43
43
|
- **Module federation** support for dynamic imports
|
|
44
|
-
- **
|
|
44
|
+
- **Bluecopa API integration** with pre-configured hooks
|
|
45
45
|
- **Production-ready** Docker configuration
|
|
46
46
|
|
|
47
47
|
### Development
|
|
@@ -114,7 +114,7 @@ If the API errors out or is not configured, the application will show "Set Envir
|
|
|
114
114
|
|
|
115
115
|
### Statement Hooks Example
|
|
116
116
|
|
|
117
|
-
This template includes a comprehensive example of using
|
|
117
|
+
This template includes a comprehensive example of using Bluecopa statement hooks at `/statements`. The example demonstrates:
|
|
118
118
|
|
|
119
119
|
- **useGetStatementData** - Fetching statement data by statement ID (with optional viewId and runId)
|
|
120
120
|
- **useGetViewsBySheetId** - Fetching all views for a statement sheet
|
|
@@ -196,7 +196,7 @@ This template comes with [Tailwind CSS v4](https://tailwindcss.com/) configured
|
|
|
196
196
|
- CSS variables for theming
|
|
197
197
|
- Dark mode support with next-themes
|
|
198
198
|
- shadcn/ui design system
|
|
199
|
-
- Custom
|
|
199
|
+
- Custom Bluecopa color palette
|
|
200
200
|
- Responsive design utilities
|
|
201
201
|
|
|
202
202
|
## Key Technologies
|
|
@@ -123,7 +123,7 @@ export default function StatementsPage() {
|
|
|
123
123
|
<div>
|
|
124
124
|
<h1 className="text-3xl font-bold tracking-tight">Statement Viewer</h1>
|
|
125
125
|
<p className="text-muted-foreground">
|
|
126
|
-
View and manage statement data using
|
|
126
|
+
View and manage statement data using Bluecopa statement hooks
|
|
127
127
|
</p>
|
|
128
128
|
</div>
|
|
129
129
|
<div className="flex gap-2">
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useEffect } from "react";
|
|
2
2
|
import { createRoot, type Root } from "react-dom/client";
|
|
3
3
|
import singleSpaReact from "single-spa-react";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
MemoryRouter,
|
|
6
|
+
BrowserRouter,
|
|
7
|
+
useNavigate,
|
|
8
|
+
useLocation,
|
|
9
|
+
} from "react-router-dom";
|
|
5
10
|
import App from "./app";
|
|
6
11
|
import type { AppProps } from "single-spa";
|
|
7
12
|
|
|
@@ -18,6 +23,93 @@ interface LifecycleProps {
|
|
|
18
23
|
|
|
19
24
|
let root: Root | null = null;
|
|
20
25
|
|
|
26
|
+
// Helper function to extract path from browser URL
|
|
27
|
+
function getInitialPath(basename?: string): string {
|
|
28
|
+
const currentPath = window.location.pathname;
|
|
29
|
+
const search = window.location.search;
|
|
30
|
+
const hash = window.location.hash;
|
|
31
|
+
|
|
32
|
+
// If basename is provided, extract the path after basename
|
|
33
|
+
let pathAfterBasename = currentPath;
|
|
34
|
+
if (basename && currentPath.startsWith(basename)) {
|
|
35
|
+
pathAfterBasename = currentPath.slice(basename.length) || "/";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Combine path, search params, and hash
|
|
39
|
+
return pathAfterBasename + search + hash;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Component to sync MemoryRouter navigation with browser URL
|
|
43
|
+
const RouterSync: React.FC<{ basename?: string }> = ({ basename }) => {
|
|
44
|
+
const location = useLocation();
|
|
45
|
+
const navigate = useNavigate();
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
// Get the current path from MemoryRouter
|
|
49
|
+
const mainAppPath = window.location.pathname;
|
|
50
|
+
if (mainAppPath.includes("/apps/ext/")) {
|
|
51
|
+
const currentPath = location.pathname;
|
|
52
|
+
const search = location.search || "";
|
|
53
|
+
const hash = location.hash || "";
|
|
54
|
+
|
|
55
|
+
// Calculate the full URL path
|
|
56
|
+
const basenamePath = basename || "";
|
|
57
|
+
const fullPath = basenamePath + currentPath + search + hash;
|
|
58
|
+
|
|
59
|
+
// Update browser URL without reloading the page
|
|
60
|
+
const currentBrowserPath =
|
|
61
|
+
window.location.pathname +
|
|
62
|
+
window.location.search +
|
|
63
|
+
window.location.hash;
|
|
64
|
+
if (currentBrowserPath !== fullPath) {
|
|
65
|
+
// Use pushState to maintain browser history for back/forward buttons
|
|
66
|
+
window.history.pushState(
|
|
67
|
+
{ ...window.history.state, path: fullPath },
|
|
68
|
+
"",
|
|
69
|
+
fullPath
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}, [location.pathname, location.search, location.hash, basename]);
|
|
74
|
+
|
|
75
|
+
// Listen for browser navigation (back/forward buttons)
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
const handlePopState = () => {
|
|
78
|
+
const mainAppPath = window.location.pathname;
|
|
79
|
+
// If current path does not contain /apps/ext/, clean up all /apps/ext/ references
|
|
80
|
+
if (mainAppPath.includes("/apps/ext/")) {
|
|
81
|
+
const currentPath = window.location.pathname;
|
|
82
|
+
const search = window.location.search || "";
|
|
83
|
+
const hash = window.location.hash || "";
|
|
84
|
+
const basenamePath = basename || "";
|
|
85
|
+
|
|
86
|
+
// Extract the path after basename
|
|
87
|
+
let pathToNavigate = currentPath;
|
|
88
|
+
if (basenamePath && currentPath.startsWith(basenamePath)) {
|
|
89
|
+
pathToNavigate = currentPath.slice(basenamePath.length) || "/";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Combine with search and hash
|
|
93
|
+
const fullPathToNavigate = pathToNavigate + search + hash;
|
|
94
|
+
const currentRouterPath =
|
|
95
|
+
location.pathname + (location.search || "") + (location.hash || "");
|
|
96
|
+
|
|
97
|
+
// Navigate MemoryRouter to match browser URL
|
|
98
|
+
if (currentRouterPath !== fullPathToNavigate) {
|
|
99
|
+
navigate(fullPathToNavigate, { replace: true });
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
window.removeEventListener("popstate", handlePopState);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
window.addEventListener("popstate", handlePopState);
|
|
107
|
+
return () => window.removeEventListener("popstate", handlePopState);
|
|
108
|
+
}, [navigate, location.pathname, location.search, location.hash, basename]);
|
|
109
|
+
|
|
110
|
+
return null;
|
|
111
|
+
};
|
|
112
|
+
|
|
21
113
|
// Root component wrapper that handles routing
|
|
22
114
|
const MicrofrontendRoot: React.FC<{
|
|
23
115
|
isMicroFrontend: boolean;
|
|
@@ -26,10 +118,17 @@ const MicrofrontendRoot: React.FC<{
|
|
|
26
118
|
// Use MemoryRouter for micro-frontend to avoid conflicts with host routing
|
|
27
119
|
// Use BrowserRouter for standalone mode
|
|
28
120
|
const Router = isMicroFrontend ? MemoryRouter : BrowserRouter;
|
|
29
|
-
|
|
121
|
+
|
|
122
|
+
// Get initial path from browser URL for MemoryRouter
|
|
123
|
+
const initialPath = isMicroFrontend ? getInitialPath(props?.basename) : "/";
|
|
124
|
+
|
|
125
|
+
const routerProps = isMicroFrontend
|
|
126
|
+
? { initialEntries: [initialPath], initialIndex: 0 }
|
|
127
|
+
: { basename: props?.basename };
|
|
30
128
|
|
|
31
129
|
return (
|
|
32
130
|
<Router {...routerProps}>
|
|
131
|
+
{isMicroFrontend && <RouterSync basename={props?.basename} />}
|
|
33
132
|
<App {...props} />
|
|
34
133
|
</Router>
|
|
35
134
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
System.register(['./__federation_fn_import-CzfA7kmP.js', './client-
|
|
1
|
+
System.register(['./__federation_fn_import-CzfA7kmP.js', './client-Dms8K6Dw.js'], (function (exports, module) {
|
|
2
2
|
'use strict';
|
|
3
|
-
var importShared, clientExports, jsxRuntimeExports, MemoryRouter, BrowserRouter, App;
|
|
3
|
+
var importShared, clientExports, jsxRuntimeExports, MemoryRouter, BrowserRouter, App, useLocation, useNavigate;
|
|
4
4
|
return {
|
|
5
5
|
setters: [module => {
|
|
6
6
|
importShared = module.importShared;
|
|
@@ -10,17 +10,81 @@ System.register(['./__federation_fn_import-CzfA7kmP.js', './client-CkHcT_xc.js']
|
|
|
10
10
|
MemoryRouter = module.M;
|
|
11
11
|
BrowserRouter = module.B;
|
|
12
12
|
App = module.A;
|
|
13
|
+
useLocation = module.u;
|
|
14
|
+
useNavigate = module.a;
|
|
13
15
|
}],
|
|
14
16
|
execute: (async function () {
|
|
15
17
|
|
|
16
18
|
function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o);}return n}function t(t){for(var n=1;n<arguments.length;n++){var r=null!=arguments[n]?arguments[n]:{};n%2?e(Object(r),true).forEach((function(e){o(t,e,r[e]);})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):e(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e));}));}return t}function n(e){return (n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:true,configurable:true,writable:true}):e[t]=n,e}function r(e){return (r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function a(e,t){var n;if("function"!=typeof(n=t.domElement?function(){return t.domElement}:t.domElementGetter?t.domElementGetter:e.domElementGetter?e.domElementGetter:function(e){var t=e.appName||e.name;if(!t)throw Error("single-spa's dom-element-getter-helpers was not given an application name as a prop, so it can't make a unique dom element container for the react application");var n="single-spa-application:".concat(t);return function(){var e=document.getElementById(n);return e||((e=document.createElement("div")).id=n,document.body.appendChild(e)),e}}(t)))throw Error("single-spa's dom-element-getter-helpers was given an invalid domElementGetter for application or parcel '".concat(t.name,"'. Expected a function, received ").concat(r(n)));return function(){var e=n(t);if(!(e instanceof HTMLElement))throw Error("single-spa's dom-element-getter-helpers: domElementGetter returned an invalid dom element for application or parcel '".concat(t.name,"'. Expected HTMLElement, received ").concat(r(e)));return e}}var c=null;try{c=require("react").createContext();}catch(e){}var u={React:null,ReactDOM:null,ReactDOMClient:null,rootComponent:null,loadRootComponent:null,errorBoundary:null,errorBoundaryClass:null,domElementGetter:null,parcelCanUpdate:true,suppressComponentDidCatchWarning:false,domElements:{},renderResults:{},updateResolves:{},unmountResolves:{}};function i(e){if("object"!==n(e))throw new Error("single-spa-react requires a configuration object");var o,r=t(t({},u),e);if(!r.React)throw new Error("single-spa-react must be passed opts.React");if(!r.ReactDOM&&!r.ReactDOMClient)throw new Error("single-spa-react must be passed opts.ReactDOM or opts.ReactDOMClient");r.renderType||(null!==(o=r.ReactDOMClient)&&void 0!==o&&o.createRoot?r.renderType="createRoot":r.renderType="render");if(!r.rootComponent&&!r.loadRootComponent)throw new Error("single-spa-react must be passed opts.rootComponent or opts.loadRootComponent");if(r.errorBoundary&&"function"!=typeof r.errorBoundary)throw Error("The errorBoundary opt for single-spa-react must either be omitted or be a function that returns React elements");!c&&r.React.createContext&&(c=r.React.createContext()),r.SingleSpaRoot=function(e){function t(e){t.displayName="SingleSpaRoot(".concat(e.name,")");}return t.prototype=Object.create(e.React.Component.prototype),t.prototype.componentDidMount=function(){setTimeout(this.props.mountFinished);},t.prototype.componentWillUnmount=function(){setTimeout(this.props.unmountFinished);},t.prototype.render=function(){return setTimeout(this.props.updateFinished),this.props.children},t}(r);var a={bootstrap:s.bind(null,r),mount:p.bind(null,r),unmount:l.bind(null,r)};return r.parcelCanUpdate&&(a.update=m.bind(null,r)),a}function s(e,t){return e.rootComponent?Promise.resolve():e.loadRootComponent(t).then((function(t){e.rootComponent=t;}))}function p(e,t){return new Promise((function(n,o){e.suppressComponentDidCatchWarning||!function(e){if(!(e&&"string"==typeof e.version&&e.version.indexOf(".")>=0))return false;var t=e.version.slice(0,e.version.indexOf("."));try{return Number(t)>=16}catch(e){return false}}(e.React)||e.errorBoundary||e.errorBoundaryClass||(e.rootComponent.prototype?e.rootComponent.prototype.componentDidCatch||console.warn("single-spa-react: ".concat(t.name||t.appName||t.childAppName,"'s rootComponent should implement componentDidCatch to avoid accidentally unmounting the entire single-spa application.")):console.warn("single-spa-react: ".concat(t.name||t.appName||t.childAppName,"'s rootComponent does not implement an error boundary. If using a functional component, consider providing an opts.errorBoundary to singleSpaReact(opts).")));var r=y(e,t,(function(){n(this);})),c=a(e,t)(),u=function(e){var t=e.reactDom,n=e.renderType,o=e.elementToRender,r=e.domElement,a=t[n];if("function"!=typeof a)throw new Error('renderType "'.concat(n,'" did not return a function.'));switch(n){case "createRoot":case "unstable_createRoot":case "createBlockingRoot":case "unstable_createBlockingRoot":var c=a(r);return c.render(o),c;case "hydrateRoot":return a(r,o);case "hydrate":default:return a(o,r),null}}({elementToRender:r,domElement:c,reactDom:d(e),renderType:f(e)});e.domElements[t.name]=c,e.renderResults[t.name]=u;}))}function l(e,t){return new Promise((function(n){e.unmountResolves[t.name]=n;var o=e.renderResults[t.name];o&&o.unmount?o.unmount():d(e).unmountComponentAtNode(e.domElements[t.name]),delete e.domElements[t.name],delete e.renderResults[t.name];}))}function m(e,t){return new Promise((function(n){e.updateResolves[t.name]||(e.updateResolves[t.name]=[]),e.updateResolves[t.name].push(n);var o=y(e,t,null),r=e.renderResults[t.name];if(r&&r.render)r.render(o);else {var c=a(e,t)();d(e).render(o,c);}}))}function d(e){return e.ReactDOMClient||e.ReactDOM}function f(e){return "function"==typeof e.renderType?e.renderType():e.renderType}function y(e,n,o){var r=e.React.createElement(e.rootComponent,n),a=c?e.React.createElement(c.Provider,{value:n},r):r;return (e.errorBoundary||n.errorBoundary||e.errorBoundaryClass||n.errorBoundaryClass)&&(e.errorBoundaryClass=e.errorBoundaryClass||n.errorBoundaryClass||function(e,t){function n(t){e.React.Component.apply(this,arguments),this.state={caughtError:null,caughtErrorInfo:null},n.displayName="SingleSpaReactErrorBoundary(".concat(t.name,")");}return n.prototype=Object.create(e.React.Component.prototype),n.prototype.render=function(){return this.state.caughtError?(e.errorBoundary||t.errorBoundary)(this.state.caughtError,this.state.caughtErrorInfo,this.props):this.props.children},n.prototype.componentDidCatch=function(e,t){this.setState({caughtError:e,caughtErrorInfo:t});},n}(e,n),a=e.React.createElement(e.errorBoundaryClass,n,a)),a=e.React.createElement(e.SingleSpaRoot,t(t({},n),{},{mountFinished:o,updateFinished:function(){e.updateResolves[n.name]&&(e.updateResolves[n.name].forEach((function(e){return e()})),delete e.updateResolves[n.name]);},unmountFinished:function(){e.unmountResolves[n.name]&&(e.unmountResolves[n.name](),delete e.unmountResolves[n.name]);}}),a)}
|
|
17
19
|
|
|
18
20
|
const React = await importShared('react');
|
|
21
|
+
const {useEffect} = React;
|
|
19
22
|
let root = null;
|
|
23
|
+
function getInitialPath(basename) {
|
|
24
|
+
const currentPath = window.location.pathname;
|
|
25
|
+
const search = window.location.search;
|
|
26
|
+
const hash = window.location.hash;
|
|
27
|
+
let pathAfterBasename = currentPath;
|
|
28
|
+
if (basename && currentPath.startsWith(basename)) {
|
|
29
|
+
pathAfterBasename = currentPath.slice(basename.length) || "/";
|
|
30
|
+
}
|
|
31
|
+
return pathAfterBasename + search + hash;
|
|
32
|
+
}
|
|
33
|
+
const RouterSync = ({ basename }) => {
|
|
34
|
+
const location = useLocation();
|
|
35
|
+
const navigate = useNavigate();
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const mainAppPath = window.location.pathname;
|
|
38
|
+
if (mainAppPath.includes("/apps/ext/")) {
|
|
39
|
+
const currentPath = location.pathname;
|
|
40
|
+
const search = location.search || "";
|
|
41
|
+
const hash = location.hash || "";
|
|
42
|
+
const basenamePath = basename || "";
|
|
43
|
+
const fullPath = basenamePath + currentPath + search + hash;
|
|
44
|
+
const currentBrowserPath = window.location.pathname + window.location.search + window.location.hash;
|
|
45
|
+
if (currentBrowserPath !== fullPath) {
|
|
46
|
+
window.history.pushState(
|
|
47
|
+
{ ...window.history.state, path: fullPath },
|
|
48
|
+
"",
|
|
49
|
+
fullPath
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}, [location.pathname, location.search, location.hash, basename]);
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
const handlePopState = () => {
|
|
56
|
+
const mainAppPath = window.location.pathname;
|
|
57
|
+
if (mainAppPath.includes("/apps/ext/")) {
|
|
58
|
+
const currentPath = window.location.pathname;
|
|
59
|
+
const search = window.location.search || "";
|
|
60
|
+
const hash = window.location.hash || "";
|
|
61
|
+
const basenamePath = basename || "";
|
|
62
|
+
let pathToNavigate = currentPath;
|
|
63
|
+
if (basenamePath && currentPath.startsWith(basenamePath)) {
|
|
64
|
+
pathToNavigate = currentPath.slice(basenamePath.length) || "/";
|
|
65
|
+
}
|
|
66
|
+
const fullPathToNavigate = pathToNavigate + search + hash;
|
|
67
|
+
const currentRouterPath = location.pathname + (location.search || "") + (location.hash || "");
|
|
68
|
+
if (currentRouterPath !== fullPathToNavigate) {
|
|
69
|
+
navigate(fullPathToNavigate, { replace: true });
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
window.removeEventListener("popstate", handlePopState);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
window.addEventListener("popstate", handlePopState);
|
|
76
|
+
return () => window.removeEventListener("popstate", handlePopState);
|
|
77
|
+
}, [navigate, location.pathname, location.search, location.hash, basename]);
|
|
78
|
+
return null;
|
|
79
|
+
};
|
|
20
80
|
const MicrofrontendRoot = ({ isMicroFrontend, props }) => {
|
|
21
81
|
const Router = isMicroFrontend ? MemoryRouter : BrowserRouter;
|
|
22
|
-
const
|
|
23
|
-
|
|
82
|
+
const initialPath = isMicroFrontend ? getInitialPath(props?.basename) : "/";
|
|
83
|
+
const routerProps = isMicroFrontend ? { initialEntries: [initialPath], initialIndex: 0 } : { basename: props?.basename };
|
|
84
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Router, { ...routerProps, children: [
|
|
85
|
+
isMicroFrontend && /* @__PURE__ */ jsxRuntimeExports.jsx(RouterSync, { basename: props?.basename }),
|
|
86
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(App, { ...props })
|
|
87
|
+
] });
|
|
24
88
|
};
|
|
25
89
|
const lifecycles = i({
|
|
26
90
|
React,
|