@reflow-js/templates 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/mfe/application.json +17 -0
- package/mfe/jest.config.js +7 -0
- package/mfe/package.json +18 -0
- package/mfe/public/index.html +21 -0
- package/mfe/reflow.config.json +35 -0
- package/mfe/src/index.ts +18 -0
- package/mfe/src/style.css +13 -0
- package/mfe/src/welcome-component.test.ts +38 -0
- package/mfe/src/welcome-component.ts +52 -0
- package/mfe/tsconfig.json +16 -0
- package/mfe-angular/README.md +14 -0
- package/mfe-angular/public/index.html +21 -0
- package/mfe-angular/reflow.config.json +23 -0
- package/mfe-angular/src/app.component.html +3 -0
- package/mfe-angular/src/components/welcome.component.ts +283 -0
- package/mfe-angular/src/index.ts +44 -0
- package/mfe-angular/src/styles.css +25 -0
- package/mfe-angular/tsconfig.json +16 -0
- package/mfe-react/README.md +0 -0
- package/mfe-react/application.json +5 -0
- package/mfe-react/public/index.html +22 -0
- package/mfe-react/reflow.config.json +34 -0
- package/mfe-react/src/App.tsx +21 -0
- package/mfe-react/src/components/welcome/welcome.component.tsx +74 -0
- package/mfe-react/src/components/welcome/welcome.style.css +224 -0
- package/mfe-react/src/index.css +25 -0
- package/mfe-react/src/index.tsx +39 -0
- package/mfe-react/tsconfig.json +19 -0
- package/mfe-react-tailwind/README.md +0 -0
- package/mfe-react-tailwind/application.json +5 -0
- package/mfe-react-tailwind/postcss.config.js +6 -0
- package/mfe-react-tailwind/public/index.html +22 -0
- package/mfe-react-tailwind/reflow.config.json +34 -0
- package/mfe-react-tailwind/src/App.tsx +21 -0
- package/mfe-react-tailwind/src/components/welcome/welcome.component.tsx +74 -0
- package/mfe-react-tailwind/src/components/welcome/welcome.style.css +224 -0
- package/mfe-react-tailwind/src/index.css +29 -0
- package/mfe-react-tailwind/src/index.tsx +39 -0
- package/mfe-react-tailwind/tailwind.config.js +11 -0
- package/mfe-react-tailwind/tsconfig.json +19 -0
- package/mfe-vue/README.md +14 -0
- package/mfe-vue/public/application.json +7 -0
- package/mfe-vue/public/index.html +21 -0
- package/mfe-vue/reflow.config.json +23 -0
- package/mfe-vue/src/App.vue +16 -0
- package/mfe-vue/src/components/Welcome.vue +307 -0
- package/mfe-vue/src/index.css +5 -0
- package/mfe-vue/src/index.ts +16 -0
- package/mfe-vue/tsconfig.json +14 -0
- package/package.json +23 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* ── Reflow brand: #222A2D / #F87202 / #E6E7E8 ── */
|
|
2
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
3
|
+
|
|
4
|
+
html, body {
|
|
5
|
+
height: 100%;
|
|
6
|
+
margin: 0;
|
|
7
|
+
padding: 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
body {
|
|
11
|
+
min-height: 100vh;
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
padding: 2rem 1rem;
|
|
16
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', system-ui, sans-serif;
|
|
17
|
+
color: #E6E7E8;
|
|
18
|
+
/* Reflow dark background with orange ambient glow */
|
|
19
|
+
background-color: #222A2D;
|
|
20
|
+
background-image:
|
|
21
|
+
radial-gradient(ellipse 600px 400px at 15% 60%, rgba(248,114,2,0.07) 0%, transparent 70%),
|
|
22
|
+
radial-gradient(ellipse 500px 300px at 85% 25%, rgba(248,114,2,0.05) 0%, transparent 65%),
|
|
23
|
+
radial-gradient(ellipse 800px 600px at 50% 100%, rgba(34,42,45,0.8) 0%, transparent 80%);
|
|
24
|
+
background-attachment: fixed;
|
|
25
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"lib": ["ES2020", "DOM"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "Node",
|
|
7
|
+
"strict": false,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"experimentalDecorators": true,
|
|
11
|
+
"emitDecoratorMetadata": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"outDir": "./dist"
|
|
14
|
+
},
|
|
15
|
+
"include": ["src"]
|
|
16
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{name}} - React MFE</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="app">
|
|
10
|
+
<!--
|
|
11
|
+
Reflow registra 'welcome-card' como Custom Element en src/index.tsx:
|
|
12
|
+
Reflow.define('welcome-card', Welcome, { shadow: true })
|
|
13
|
+
El Shadow DOM encapsula los estilos del componente React.
|
|
14
|
+
-->
|
|
15
|
+
<welcome-card
|
|
16
|
+
title="{{name}} está corriendo"
|
|
17
|
+
description="Componente React encapsulado con Shadow DOM por Reflow-js."
|
|
18
|
+
name="Developer">
|
|
19
|
+
</welcome-card>
|
|
20
|
+
</div>
|
|
21
|
+
</body>
|
|
22
|
+
</html>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{name}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"buildEngine": "webpack",
|
|
5
|
+
"bundler": {
|
|
6
|
+
"input": "src/index.tsx",
|
|
7
|
+
"output": "dist",
|
|
8
|
+
"format": "umd",
|
|
9
|
+
"minify": false,
|
|
10
|
+
"sourcemap": true
|
|
11
|
+
},
|
|
12
|
+
"components": {
|
|
13
|
+
"srcDir": "src/components",
|
|
14
|
+
"outDir": "dist/components",
|
|
15
|
+
"prefix": "mfe",
|
|
16
|
+
"registry": "dist/registry.js"
|
|
17
|
+
},
|
|
18
|
+
"dev": {
|
|
19
|
+
"port": 3001,
|
|
20
|
+
"hotReload": true,
|
|
21
|
+
"open": true
|
|
22
|
+
},
|
|
23
|
+
"devServer": {
|
|
24
|
+
"historyApiFallback": true,
|
|
25
|
+
"static": {
|
|
26
|
+
"directory": "./"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"styling": {
|
|
30
|
+
"system": "native",
|
|
31
|
+
"shadowDOM": true,
|
|
32
|
+
"cssVariables": true
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* App.tsx
|
|
5
|
+
* Este archivo no se usa directamente — Reflow monta el componente
|
|
6
|
+
* Welcome directamente como Web Component en public/index.html.
|
|
7
|
+
*
|
|
8
|
+
* Puedes usar este archivo para componer múltiples Web Components
|
|
9
|
+
* definidos via Reflow.define() en src/index.tsx.
|
|
10
|
+
*/
|
|
11
|
+
const App: React.FC = () => {
|
|
12
|
+
return (
|
|
13
|
+
<main style={{ display: 'flex', justifyContent: 'center', padding: '2rem' }}>
|
|
14
|
+
{/* welcome-card es registrado por Reflow en index.tsx:
|
|
15
|
+
Reflow.define('welcome-card', Welcome, { name: 'User' }, { shadow: true }) */}
|
|
16
|
+
<welcome-card title="{{name}} App" name="Developer" />
|
|
17
|
+
</main>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default App;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import './welcome.style.css';
|
|
3
|
+
|
|
4
|
+
interface WelcomeProps {
|
|
5
|
+
title?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Welcome — Componente React registrado como Web Component via Reflow.define()
|
|
12
|
+
* El Shadow DOM (shadow: true) encapsula estilos y DOM de forma nativa.
|
|
13
|
+
*/
|
|
14
|
+
export const Welcome: React.FC<WelcomeProps> = ({
|
|
15
|
+
title = 'Reflow-js React MFE',
|
|
16
|
+
description = 'Este componente React está encapsulado por Reflow como Web Component con Shadow DOM.',
|
|
17
|
+
name = 'Developer',
|
|
18
|
+
}) => {
|
|
19
|
+
return (
|
|
20
|
+
<div className="rf-card">
|
|
21
|
+
{/* Ambient glow */}
|
|
22
|
+
<div className="rf-glow"></div>
|
|
23
|
+
|
|
24
|
+
{/* Top border accent */}
|
|
25
|
+
<div className="rf-card__top-bar"></div>
|
|
26
|
+
|
|
27
|
+
<div className="rf-card__inner">
|
|
28
|
+
{/* Header row: icon + badge */}
|
|
29
|
+
<div className="rf-card__header">
|
|
30
|
+
<div className="rf-icon-wrap">⚛️</div>
|
|
31
|
+
<span className="rf-badge">React MFE</span>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
{/* Title & description */}
|
|
35
|
+
<h1 className="rf-card__title">{title}</h1>
|
|
36
|
+
<p className="rf-card__desc">{description}</p>
|
|
37
|
+
|
|
38
|
+
{/* Divider */}
|
|
39
|
+
<div className="rf-divider"></div>
|
|
40
|
+
|
|
41
|
+
{/* Greeting */}
|
|
42
|
+
<div className="rf-greeting">
|
|
43
|
+
<div className="rf-avatar">{name.charAt(0).toUpperCase()}</div>
|
|
44
|
+
<span className="rf-greeting__text">Hola, <strong className="rf-greeting__name">{name}</strong></span>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
{/* Actions */}
|
|
48
|
+
<div className="rf-actions">
|
|
49
|
+
<button
|
|
50
|
+
className="rf-btn rf-btn--primary"
|
|
51
|
+
onClick={() => console.log('[Reflow] welcome-card clicked')}
|
|
52
|
+
>
|
|
53
|
+
<svg className="rf-btn__ico" viewBox="0 0 20 20" fill="currentColor"><path fillRule="evenodd" d="M11.3 1.046A1 1 0 0112 2v5h4a1 1 0 01.82 1.573l-7 10A1 1 0 018 18v-5H4a1 1 0 01-.82-1.573l7-10a1 1 0 011.12-.38z" clipRule="evenodd"/></svg>
|
|
54
|
+
Acción del MFE
|
|
55
|
+
</button>
|
|
56
|
+
<button
|
|
57
|
+
className="rf-btn rf-btn--ghost"
|
|
58
|
+
onClick={() => alert(`¡Shadow DOM activo! Estilos de '${title}' están encapsulados.`)}
|
|
59
|
+
>
|
|
60
|
+
<svg className="rf-btn__ico" viewBox="0 0 20 20" fill="currentColor"><path fillRule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clipRule="evenodd"/></svg>
|
|
61
|
+
Shadow DOM
|
|
62
|
+
</button>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
{/* Footer chips */}
|
|
66
|
+
<div className="rf-chips">
|
|
67
|
+
<span className="rf-chip rf-chip--blue">React 18</span>
|
|
68
|
+
<span className="rf-chip rf-chip--muted">Reflow-js</span>
|
|
69
|
+
<span className="rf-chip rf-chip--outline">Shadow DOM</span>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
};
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/* welcome.style.css
|
|
2
|
+
* Estilos nativos del componente — encapsulados por Shadow DOM
|
|
3
|
+
* cuando se usa Reflow.define() con { shadow: true }
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* ── Brand tokens (React Blue) ── */
|
|
7
|
+
:host {
|
|
8
|
+
--rf-blue: #0EA5E9;
|
|
9
|
+
--rf-dark: #222A2D;
|
|
10
|
+
--rf-light: #E6E7E8;
|
|
11
|
+
--rf-surface: #2A3438;
|
|
12
|
+
--rf-border: rgba(14, 165, 233, 0.18);
|
|
13
|
+
--rf-muted: rgba(230, 231, 232, 0.45);
|
|
14
|
+
|
|
15
|
+
display: flex;
|
|
16
|
+
justify-content: center;
|
|
17
|
+
align-items: center;
|
|
18
|
+
width: 100%;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* ── Card shell ── */
|
|
22
|
+
.rf-card {
|
|
23
|
+
position: relative;
|
|
24
|
+
width: 100%;
|
|
25
|
+
max-width: 440px;
|
|
26
|
+
border-radius: 1.25rem;
|
|
27
|
+
background: var(--rf-surface);
|
|
28
|
+
border: 1px solid var(--rf-border);
|
|
29
|
+
box-shadow:
|
|
30
|
+
0 0 0 1px rgba(14,165,233,0.06),
|
|
31
|
+
0 24px 64px rgba(0,0,0,0.55),
|
|
32
|
+
0 4px 16px rgba(0,0,0,0.35);
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', system-ui, sans-serif;
|
|
35
|
+
transition: transform 0.25s ease, box-shadow 0.25s ease;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.rf-card:hover {
|
|
39
|
+
transform: translateY(-3px);
|
|
40
|
+
box-shadow:
|
|
41
|
+
0 0 0 1px rgba(14,165,233,0.14),
|
|
42
|
+
0 32px 80px rgba(0,0,0,0.6),
|
|
43
|
+
0 8px 24px rgba(14,165,233,0.12);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* ambient blue glow */
|
|
47
|
+
.rf-glow {
|
|
48
|
+
position: absolute;
|
|
49
|
+
top: -60px; left: 50%;
|
|
50
|
+
transform: translateX(-50%);
|
|
51
|
+
width: 260px; height: 120px;
|
|
52
|
+
background: radial-gradient(ellipse, rgba(14,165,233,0.18) 0%, transparent 70%);
|
|
53
|
+
pointer-events: none;
|
|
54
|
+
z-index: 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* top accent line */
|
|
58
|
+
.rf-card__top-bar {
|
|
59
|
+
height: 3px;
|
|
60
|
+
background: linear-gradient(90deg, transparent 0%, var(--rf-blue) 40%, #06B6D4 60%, transparent 100%);
|
|
61
|
+
position: relative;
|
|
62
|
+
z-index: 1;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.rf-card__inner {
|
|
66
|
+
position: relative;
|
|
67
|
+
z-index: 1;
|
|
68
|
+
padding: 2rem 2rem 1.75rem;
|
|
69
|
+
display: flex;
|
|
70
|
+
flex-direction: column;
|
|
71
|
+
gap: 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* ── Header row ── */
|
|
75
|
+
.rf-card__header {
|
|
76
|
+
display: flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
gap: 0.75rem;
|
|
79
|
+
margin-bottom: 1.5rem;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.rf-icon-wrap {
|
|
83
|
+
width: 42px; height: 42px;
|
|
84
|
+
border-radius: 0.75rem;
|
|
85
|
+
display: flex;
|
|
86
|
+
align-items: center;
|
|
87
|
+
justify-content: center;
|
|
88
|
+
flex-shrink: 0;
|
|
89
|
+
font-size: 1.75rem;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.rf-badge {
|
|
93
|
+
font-size: 0.7rem;
|
|
94
|
+
font-weight: 700;
|
|
95
|
+
letter-spacing: 0.08em;
|
|
96
|
+
text-transform: uppercase;
|
|
97
|
+
color: var(--rf-blue);
|
|
98
|
+
background: rgba(14,165,233,0.10);
|
|
99
|
+
border: 1px solid rgba(14,165,233,0.20);
|
|
100
|
+
padding: 0.22rem 0.7rem;
|
|
101
|
+
border-radius: 999px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* ── Typography ── */
|
|
105
|
+
.rf-card__title {
|
|
106
|
+
margin: 0 0 0.6rem;
|
|
107
|
+
font-size: 1.5rem;
|
|
108
|
+
font-weight: 800;
|
|
109
|
+
color: var(--rf-light);
|
|
110
|
+
letter-spacing: -0.03em;
|
|
111
|
+
line-height: 1.2;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.rf-card__desc {
|
|
115
|
+
margin: 0;
|
|
116
|
+
font-size: 0.875rem;
|
|
117
|
+
color: var(--rf-muted);
|
|
118
|
+
line-height: 1.7;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* ── Divider ── */
|
|
122
|
+
.rf-divider {
|
|
123
|
+
height: 1px;
|
|
124
|
+
background: linear-gradient(90deg, transparent, rgba(230,231,232,0.10), transparent);
|
|
125
|
+
margin: 1.5rem 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* ── Greeting ── */
|
|
129
|
+
.rf-greeting {
|
|
130
|
+
display: flex;
|
|
131
|
+
align-items: center;
|
|
132
|
+
gap: 0.75rem;
|
|
133
|
+
margin-bottom: 1.75rem;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.rf-avatar {
|
|
137
|
+
width: 36px; height: 36px;
|
|
138
|
+
border-radius: 50%;
|
|
139
|
+
background: linear-gradient(135deg, var(--rf-blue), #06B6D4);
|
|
140
|
+
color: #fff;
|
|
141
|
+
font-size: 0.875rem;
|
|
142
|
+
font-weight: 800;
|
|
143
|
+
display: flex;
|
|
144
|
+
align-items: center;
|
|
145
|
+
justify-content: center;
|
|
146
|
+
flex-shrink: 0;
|
|
147
|
+
box-shadow: 0 2px 8px rgba(14,165,233,0.40);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.rf-greeting__text { font-size: 0.9rem; color: var(--rf-muted); }
|
|
151
|
+
.rf-greeting__name { color: var(--rf-light); font-weight: 700; }
|
|
152
|
+
|
|
153
|
+
/* ── Buttons ── */
|
|
154
|
+
.rf-actions {
|
|
155
|
+
display: flex;
|
|
156
|
+
gap: 0.75rem;
|
|
157
|
+
margin-bottom: 1.75rem;
|
|
158
|
+
flex-wrap: wrap;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.rf-btn {
|
|
162
|
+
display: inline-flex;
|
|
163
|
+
align-items: center;
|
|
164
|
+
gap: 0.45rem;
|
|
165
|
+
padding: 0.6rem 1.25rem;
|
|
166
|
+
border-radius: 0.65rem;
|
|
167
|
+
font-size: 0.85rem;
|
|
168
|
+
font-weight: 600;
|
|
169
|
+
cursor: pointer;
|
|
170
|
+
border: none;
|
|
171
|
+
transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
|
|
172
|
+
letter-spacing: 0.01em;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.rf-btn__ico { width: 14px; height: 14px; flex-shrink: 0; }
|
|
176
|
+
|
|
177
|
+
.rf-btn--primary {
|
|
178
|
+
background: var(--rf-blue);
|
|
179
|
+
color: #fff;
|
|
180
|
+
box-shadow: 0 4px 16px rgba(14,165,233,0.40);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.rf-btn--primary:hover {
|
|
184
|
+
background: #0284C7;
|
|
185
|
+
transform: translateY(-2px);
|
|
186
|
+
box-shadow: 0 8px 24px rgba(14,165,233,0.50);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.rf-btn--primary:active { transform: translateY(0); }
|
|
190
|
+
|
|
191
|
+
.rf-btn--ghost {
|
|
192
|
+
background: rgba(230,231,232,0.06);
|
|
193
|
+
color: var(--rf-light);
|
|
194
|
+
border: 1px solid rgba(230,231,232,0.14);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.rf-btn--ghost:hover {
|
|
198
|
+
background: rgba(230,231,232,0.10);
|
|
199
|
+
border-color: rgba(230,231,232,0.24);
|
|
200
|
+
transform: translateY(-2px);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.rf-btn--ghost:active { transform: translateY(0); }
|
|
204
|
+
|
|
205
|
+
/* ── Chips ── */
|
|
206
|
+
.rf-chips {
|
|
207
|
+
display: flex;
|
|
208
|
+
gap: 0.5rem;
|
|
209
|
+
flex-wrap: wrap;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.rf-chip {
|
|
213
|
+
display: inline-block;
|
|
214
|
+
padding: 0.18rem 0.6rem;
|
|
215
|
+
border-radius: 999px;
|
|
216
|
+
font-size: 0.68rem;
|
|
217
|
+
font-weight: 600;
|
|
218
|
+
letter-spacing: 0.05em;
|
|
219
|
+
text-transform: uppercase;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.rf-chip--blue { background: rgba(14,165,233,0.12); color: var(--rf-blue); border: 1px solid rgba(14,165,233,0.25); }
|
|
223
|
+
.rf-chip--muted { background: rgba(230,231,232,0.07); color: var(--rf-muted); border: 1px solid rgba(230,231,232,0.12); }
|
|
224
|
+
.rf-chip--outline { background: transparent; color: rgba(230,231,232,0.35); border: 1px solid rgba(230,231,232,0.12); }
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* ── React brand: #222A2D / #0EA5E9 / #E6E7E8 ── */
|
|
2
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
3
|
+
|
|
4
|
+
html, body {
|
|
5
|
+
height: 100%;
|
|
6
|
+
margin: 0;
|
|
7
|
+
padding: 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
body {
|
|
11
|
+
min-height: 100vh;
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
padding: 2rem 1rem;
|
|
16
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', system-ui, sans-serif;
|
|
17
|
+
color: #E6E7E8;
|
|
18
|
+
/* Dark background with blue ambient glow */
|
|
19
|
+
background-color: #222A2D;
|
|
20
|
+
background-image:
|
|
21
|
+
radial-gradient(ellipse 600px 400px at 15% 60%, rgba(14,165,233,0.08) 0%, transparent 70%),
|
|
22
|
+
radial-gradient(ellipse 500px 300px at 85% 25%, rgba(6,182,212,0.06) 0%, transparent 65%),
|
|
23
|
+
radial-gradient(ellipse 800px 600px at 50% 100%, rgba(34,42,45,0.8) 0%, transparent 80%);
|
|
24
|
+
background-attachment: fixed;
|
|
25
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import * as ReactDOM from 'react-dom/client';
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
import { Reflow } from '@reflow-js/core';
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
import { Welcome } from './components/welcome/welcome.component';
|
|
7
|
+
import './index.css';
|
|
8
|
+
|
|
9
|
+
declare global {
|
|
10
|
+
namespace JSX {
|
|
11
|
+
interface IntrinsicElements {
|
|
12
|
+
'welcome-card': any; // Usamos el tag definido en el registry del MFE
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Inicializa Reflow inyectando las dependencias de React
|
|
18
|
+
// Esto evita múltiples instancias de React entre host y MFEs
|
|
19
|
+
Reflow.init({
|
|
20
|
+
propertiesFile: 'application.json',
|
|
21
|
+
react: React,
|
|
22
|
+
reactDom: ReactDOM,
|
|
23
|
+
}).then(() => {
|
|
24
|
+
console.log('[Reflow] ✅ Inicializado correctamente');
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Reflow.define() convierte un componente React en un Custom Element.
|
|
28
|
+
*
|
|
29
|
+
* Firma: Reflow.define(tagName, Component, defaultProps, options)
|
|
30
|
+
*
|
|
31
|
+
* options.shadow: true → Shadow DOM activo → estilos encapsulados
|
|
32
|
+
* Las props se pueden pasar como atributos HTML: <welcome-card title="Hola">
|
|
33
|
+
*/
|
|
34
|
+
Reflow.define('welcome-card', Welcome, { name: 'User' }, { shadow: true });
|
|
35
|
+
|
|
36
|
+
console.log('[Reflow] 🧩 welcome-card registrado como Web Component');
|
|
37
|
+
}).catch((e: any) => {
|
|
38
|
+
console.error('[Reflow] ❌ Error en inicialización', e);
|
|
39
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"allowSyntheticDefaultImports": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"module": "ESNext",
|
|
12
|
+
"moduleResolution": "Node",
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"isolatedModules": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"jsx": "react-jsx"
|
|
17
|
+
},
|
|
18
|
+
"include": ["src"]
|
|
19
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{name}} - React MFE</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="app">
|
|
10
|
+
<!--
|
|
11
|
+
Reflow registra 'welcome-card' como Custom Element en src/index.tsx:
|
|
12
|
+
Reflow.define('welcome-card', Welcome, { shadow: true })
|
|
13
|
+
El Shadow DOM encapsula los estilos del componente React.
|
|
14
|
+
-->
|
|
15
|
+
<welcome-card
|
|
16
|
+
title="{{name}} está corriendo"
|
|
17
|
+
description="Componente React encapsulado con Shadow DOM por Reflow-js."
|
|
18
|
+
name="Developer">
|
|
19
|
+
</welcome-card>
|
|
20
|
+
</div>
|
|
21
|
+
</body>
|
|
22
|
+
</html>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{name}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"buildEngine": "webpack",
|
|
5
|
+
"bundler": {
|
|
6
|
+
"input": "src/index.tsx",
|
|
7
|
+
"output": "dist",
|
|
8
|
+
"format": "umd",
|
|
9
|
+
"minify": false,
|
|
10
|
+
"sourcemap": true
|
|
11
|
+
},
|
|
12
|
+
"components": {
|
|
13
|
+
"srcDir": "src/components",
|
|
14
|
+
"outDir": "dist/components",
|
|
15
|
+
"prefix": "mfe",
|
|
16
|
+
"registry": "dist/registry.js"
|
|
17
|
+
},
|
|
18
|
+
"dev": {
|
|
19
|
+
"port": 3001,
|
|
20
|
+
"hotReload": true,
|
|
21
|
+
"open": true
|
|
22
|
+
},
|
|
23
|
+
"devServer": {
|
|
24
|
+
"historyApiFallback": true,
|
|
25
|
+
"static": {
|
|
26
|
+
"directory": "./"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"styling": {
|
|
30
|
+
"system": "tailwind",
|
|
31
|
+
"shadowDOM": true,
|
|
32
|
+
"cssVariables": true
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* App.tsx
|
|
5
|
+
* Este archivo no se usa directamente — Reflow monta el componente
|
|
6
|
+
* Welcome directamente como Web Component en public/index.html.
|
|
7
|
+
*
|
|
8
|
+
* Puedes usar este archivo para componer múltiples Web Components
|
|
9
|
+
* definidos via Reflow.define() en src/index.tsx.
|
|
10
|
+
*/
|
|
11
|
+
const App: React.FC = () => {
|
|
12
|
+
return (
|
|
13
|
+
<main style={{ display: 'flex', justifyContent: 'center', padding: '2rem' }}>
|
|
14
|
+
{/* welcome-card es registrado por Reflow en index.tsx:
|
|
15
|
+
Reflow.define('welcome-card', Welcome, { name: 'User' }, { shadow: true }) */}
|
|
16
|
+
<welcome-card title="{{name}} App" name="Developer" />
|
|
17
|
+
</main>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default App;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import './welcome.style.css';
|
|
3
|
+
|
|
4
|
+
interface WelcomeProps {
|
|
5
|
+
title?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Welcome — Componente React registrado como Web Component via Reflow.define()
|
|
12
|
+
* El Shadow DOM (shadow: true) encapsula estilos y DOM de forma nativa.
|
|
13
|
+
*/
|
|
14
|
+
export const Welcome: React.FC<WelcomeProps> = ({
|
|
15
|
+
title = 'Reflow-js React MFE',
|
|
16
|
+
description = 'Este componente React está encapsulado por Reflow como Web Component con Shadow DOM.',
|
|
17
|
+
name = 'Developer',
|
|
18
|
+
}) => {
|
|
19
|
+
return (
|
|
20
|
+
<div className="rf-card">
|
|
21
|
+
{/* Ambient glow */}
|
|
22
|
+
<div className="rf-glow"></div>
|
|
23
|
+
|
|
24
|
+
{/* Top border accent */}
|
|
25
|
+
<div className="rf-card__top-bar"></div>
|
|
26
|
+
|
|
27
|
+
<div className="rf-card__inner">
|
|
28
|
+
{/* Header row: icon + badge */}
|
|
29
|
+
<div className="rf-card__header">
|
|
30
|
+
<div className="rf-icon-wrap">⚛️</div>
|
|
31
|
+
<span className="rf-badge">React MFE</span>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
{/* Title & description */}
|
|
35
|
+
<h1 className="rf-card__title">{title}</h1>
|
|
36
|
+
<p className="rf-card__desc">{description}</p>
|
|
37
|
+
|
|
38
|
+
{/* Divider */}
|
|
39
|
+
<div className="rf-divider"></div>
|
|
40
|
+
|
|
41
|
+
{/* Greeting */}
|
|
42
|
+
<div className="rf-greeting">
|
|
43
|
+
<div className="rf-avatar">{name.charAt(0).toUpperCase()}</div>
|
|
44
|
+
<span className="rf-greeting__text">Hola, <strong className="rf-greeting__name">{name}</strong></span>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
{/* Actions */}
|
|
48
|
+
<div className="rf-actions">
|
|
49
|
+
<button
|
|
50
|
+
className="rf-btn rf-btn--primary"
|
|
51
|
+
onClick={() => console.log('[Reflow] welcome-card clicked')}
|
|
52
|
+
>
|
|
53
|
+
<svg className="rf-btn__ico" viewBox="0 0 20 20" fill="currentColor"><path fillRule="evenodd" d="M11.3 1.046A1 1 0 0112 2v5h4a1 1 0 01.82 1.573l-7 10A1 1 0 018 18v-5H4a1 1 0 01-.82-1.573l7-10a1 1 0 011.12-.38z" clipRule="evenodd"/></svg>
|
|
54
|
+
Acción del MFE
|
|
55
|
+
</button>
|
|
56
|
+
<button
|
|
57
|
+
className="rf-btn rf-btn--ghost"
|
|
58
|
+
onClick={() => alert(`¡Shadow DOM activo! Estilos de '${title}' están encapsulados.`)}
|
|
59
|
+
>
|
|
60
|
+
<svg className="rf-btn__ico" viewBox="0 0 20 20" fill="currentColor"><path fillRule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clipRule="evenodd"/></svg>
|
|
61
|
+
Shadow DOM
|
|
62
|
+
</button>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
{/* Footer chips */}
|
|
66
|
+
<div className="rf-chips">
|
|
67
|
+
<span className="rf-chip rf-chip--blue">React 18</span>
|
|
68
|
+
<span className="rf-chip rf-chip--muted">Reflow-js</span>
|
|
69
|
+
<span className="rf-chip rf-chip--outline">Shadow DOM</span>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
};
|