@africode/core 5.0.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/AFRICODE_FRAMEWORK_GUIDE.md +707 -0
- package/LICENSE +623 -0
- package/README.md +442 -0
- package/bin/africode.js +73 -0
- package/bin/africode.js.1758507140 +343 -0
- package/bin/cli.ts +83 -0
- package/bin/create-africode.js +158 -0
- package/bin/scaffold.ts +219 -0
- package/components/accordion.js +183 -0
- package/components/alert.js +131 -0
- package/components/auth.js +172 -0
- package/components/avatar.js +117 -0
- package/components/badge.js +104 -0
- package/components/base.d.ts +139 -0
- package/components/base.js +184 -0
- package/components/button.js +164 -0
- package/components/card.js +137 -0
- package/components/cultural-card.js +243 -0
- package/components/divider.js +83 -0
- package/components/dropdown.js +171 -0
- package/components/error-boundary.js +155 -0
- package/components/form.js +131 -0
- package/components/grid.js +273 -0
- package/components/hero.js +138 -0
- package/components/icon.js +36 -0
- package/components/index.js +57 -0
- package/components/input.js +256 -0
- package/components/kanga-card.js +185 -0
- package/components/language-switcher.js +108 -0
- package/components/loader.js +80 -0
- package/components/modal.js +262 -0
- package/components/motion.js +84 -0
- package/components/navbar.js +236 -0
- package/components/pattern-showcase.js +225 -0
- package/components/progress.js +134 -0
- package/components/react.js +111 -0
- package/components/section.js +54 -0
- package/components/select.js +322 -0
- package/components/sidebar.js +180 -0
- package/components/skeleton.js +85 -0
- package/components/table.js +181 -0
- package/components/tabs.js +202 -0
- package/components/theme-toggle.js +82 -0
- package/components/toast.js +139 -0
- package/components/tooltip.js +167 -0
- package/core/a2ui-schema-manager.js +344 -0
- package/core/a2ui.js +431 -0
- package/core/bun-runtime.js +799 -0
- package/core/cli/commands/add.js +23 -0
- package/core/cli/commands/audit.js +58 -0
- package/core/cli/commands/build.js +137 -0
- package/core/cli/commands/create-plugin.js +241 -0
- package/core/cli/commands/dev.js +228 -0
- package/core/cli/commands/lint.js +23 -0
- package/core/cli/commands/test.js +34 -0
- package/core/cli/migrator.js +71 -0
- package/core/cli/ui.js +46 -0
- package/core/compliance.js +628 -0
- package/core/config.js +263 -0
- package/core/db-advanced.js +481 -0
- package/core/db.js +284 -0
- package/core/enhanced-hmr.js +404 -0
- package/core/errors.js +222 -0
- package/core/file-router.js +290 -0
- package/core/heartbeat.js +64 -0
- package/core/hmr-client.js +204 -0
- package/core/hmr.js +196 -0
- package/core/html.d.ts +116 -0
- package/core/html.js +160 -0
- package/core/hydration.js +52 -0
- package/core/lipa-namba-journey.js +572 -0
- package/core/motion.js +106 -0
- package/core/nida-cig-middleware.js +455 -0
- package/core/patterns.d.ts +124 -0
- package/core/patterns.js +833 -0
- package/core/plugins/index.js +312 -0
- package/core/router.js +387 -0
- package/core/sdk-client.js +62 -0
- package/core/sdk.d.ts +133 -0
- package/core/sdk.js +123 -0
- package/core/seo.js +76 -0
- package/core/server/auth-endpoints.js +339 -0
- package/core/server/auth.js +180 -0
- package/core/server/csrf.js +206 -0
- package/core/server/db.js +39 -0
- package/core/server/middleware.js +324 -0
- package/core/server/rate-limit.js +238 -0
- package/core/server/render.js +69 -0
- package/core/server/router.js +120 -0
- package/core/shim.js +28 -0
- package/core/state.d.ts +86 -0
- package/core/state.js +242 -0
- package/core/store.d.ts +122 -0
- package/core/store.js +61 -0
- package/core/validation.d.ts +233 -0
- package/core/validation.js +590 -0
- package/core/websocket.js +639 -0
- package/dist/africode.js +2905 -0
- package/dist/africode.js.map +61 -0
- package/dist/build-info.json +23 -0
- package/dist/components.js +2888 -0
- package/dist/components.js.map +58 -0
- package/dist/styles/africanity.css +322 -0
- package/dist/styles/typography.css +141 -0
- package/docs/IDE-Guide.md +50 -0
- package/package.json +110 -0
- package/src/index.ts +196 -0
- package/styles/africanity.css +322 -0
- package/styles/typography.css +141 -0
- package/templates/starter/.env.example +15 -0
- package/templates/starter/africode.config.js +40 -0
- package/templates/starter/package.json +14 -0
- package/templates/starter/src/pages/index.html +46 -0
- package/templates/starter/src/pages/index.js +32 -0
- package/templates/starter/src/styles/main.css +4 -0
- package/templates/starter-3d/.env.example +7 -0
- package/templates/starter-3d/africode.config.js +29 -0
- package/templates/starter-3d/components/af-model-viewer.js +125 -0
- package/templates/starter-3d/package.json +15 -0
- package/templates/starter-3d/src/pages/index.html +46 -0
- package/templates/starter-3d/src/pages/index.js +50 -0
- package/templates/starter-3d/src/styles/main.css +4 -0
- package/templates/starter-react/.env.example +15 -0
- package/templates/starter-react/africode.config.js +40 -0
- package/templates/starter-react/package.json +16 -0
- package/templates/starter-react/src/pages/index.html +46 -0
- package/templates/starter-react/src/pages/index.js +68 -0
- package/templates/starter-react/src/styles/main.css +4 -0
- package/templates/starter-tailwind/.env.example +15 -0
- package/templates/starter-tailwind/africode.config.js +40 -0
- package/templates/starter-tailwind/package.json +20 -0
- package/templates/starter-tailwind/src/pages/index.html +46 -0
- package/templates/starter-tailwind/src/pages/index.js +37 -0
- package/templates/starter-tailwind/src/styles/main.css +4 -0
- package/templates/starter-tailwind/src/styles/tailwind.css +1 -0
- package/templates/starter-tailwind/src/tailwind-loader.js +30 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AfriCode Pattern Showcase Component
|
|
3
|
+
*
|
|
4
|
+
* Interactive gallery displaying all African cultural patterns
|
|
5
|
+
* with hover effects and pattern information.
|
|
6
|
+
*
|
|
7
|
+
* @module components/pattern-showcase
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { AfriCodeComponent, registerComponent } from './base.js';
|
|
11
|
+
import * as patterns from '../core/patterns.js';
|
|
12
|
+
|
|
13
|
+
export class PatternShowcase extends AfriCodeComponent {
|
|
14
|
+
static get observedAttributes() {
|
|
15
|
+
return ['region', 'size'];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
super();
|
|
20
|
+
this._allPatterns = [
|
|
21
|
+
// East Africa
|
|
22
|
+
{ key: 'shuka', name: 'Maasai Shuka', region: 'east', country: 'Tanzania/Kenya', fn: patterns.generateShuka, meaning: 'Bravery, Unity, Identity' },
|
|
23
|
+
{ key: 'hadzabe', name: 'Hadzabe', region: 'east', country: 'Tanzania', fn: patterns.generateHadzabe, meaning: 'Ancient traditions, Hunter-gatherer heritage' },
|
|
24
|
+
{ key: 'kanga', name: 'Kanga', region: 'east', country: 'Tanzania/Kenya', fn: patterns.generateKangaBorder, meaning: 'Communication, Proverbs, Femininity' },
|
|
25
|
+
{ key: 'kitenge', name: 'Kitenge', region: 'east', country: 'East Africa', fn: patterns.generateKitenge, meaning: 'Celebration, African identity' },
|
|
26
|
+
{ key: 'tibeb', name: 'Tibeb', region: 'east', country: 'Ethiopia', fn: patterns.generateTibeb, meaning: 'Faith, Tradition, Celebration' },
|
|
27
|
+
// West Africa
|
|
28
|
+
{ key: 'kente', name: 'Kente', region: 'west', country: 'Ghana', fn: patterns.generateKente, meaning: 'Royalty, Wisdom, Celebration' },
|
|
29
|
+
{ key: 'adinkra', name: 'Adinkra', region: 'west', country: 'Ghana', fn: patterns.generateAdinkra, meaning: 'Philosophy, Proverbs, Wisdom' },
|
|
30
|
+
{ key: 'bogolan', name: 'Bogolan', region: 'west', country: 'Mali', fn: patterns.generateBogolan, meaning: 'Earth energy, Protection, Healing' },
|
|
31
|
+
{ key: 'asooke', name: 'Aso-Oke', region: 'west', country: 'Nigeria', fn: patterns.generateAsoOke, meaning: 'Prestige, Celebration, Heritage' },
|
|
32
|
+
{ key: 'adire', name: 'Adire (Indigo)', region: 'west', country: 'Nigeria', fn: patterns.generateAdire, meaning: 'Creativity, Uniqueness, Depth' },
|
|
33
|
+
{ key: 'ankara', name: 'Ankara', region: 'west', country: 'West Africa', fn: patterns.generateAnkara, meaning: 'Boldness, Pan-African pride' },
|
|
34
|
+
// Central Africa
|
|
35
|
+
{ key: 'kuba', name: 'Kuba Shoowa', region: 'central', country: 'DRC', fn: patterns.generateKuba, meaning: 'Status, Mathematics, Eternity' },
|
|
36
|
+
{ key: 'imigongo', name: 'Imigongo', region: 'central', country: 'Rwanda', fn: patterns.generateImigongo, meaning: 'Resilience, Strength, Artistry' },
|
|
37
|
+
// North Africa
|
|
38
|
+
{ key: 'berber', name: 'Berber', region: 'north', country: 'North Africa', fn: patterns.generateBerber, meaning: 'Protection, Nature, Identity' },
|
|
39
|
+
// Southern Africa
|
|
40
|
+
{ key: 'ndebele', name: 'Ndebele', region: 'south', country: 'South Africa', fn: patterns.generateNdebele, meaning: 'Artistry, Marriage, Status' },
|
|
41
|
+
{ key: 'zulu', name: 'Zulu Beadwork', region: 'south', country: 'South Africa', fn: patterns.generateZulu, meaning: 'Love letters, Community' },
|
|
42
|
+
{ key: 'swazi', name: 'Swazi', region: 'south', country: 'Eswatini', fn: patterns.generateSwazi, meaning: 'Monarchy, Warriors, Pride' },
|
|
43
|
+
{ key: 'xhosa', name: 'Xhosa', region: 'south', country: 'South Africa', fn: patterns.generateXhosa, meaning: 'Dignity, Coming of age' }
|
|
44
|
+
];
|
|
45
|
+
this.render();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
connectedCallback() {
|
|
49
|
+
this._setupHovers();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_setupHovers() {
|
|
53
|
+
this.shadowRoot.querySelectorAll('.pattern-item').forEach(item => {
|
|
54
|
+
item.addEventListener('click', () => {
|
|
55
|
+
const key = item.dataset.key;
|
|
56
|
+
const pattern = this._allPatterns.find(p => p.key === key);
|
|
57
|
+
if (pattern) {
|
|
58
|
+
this.emit('af-pattern-select', { pattern });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
attributeChangedCallback() {
|
|
65
|
+
this.render();
|
|
66
|
+
this._setupHovers();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
render() {
|
|
70
|
+
const regionFilter = this.getAttribute('region') || 'all';
|
|
71
|
+
const size = this.getAttribute('size') || 'md';
|
|
72
|
+
|
|
73
|
+
const filtered = regionFilter === 'all'
|
|
74
|
+
? this._allPatterns
|
|
75
|
+
: this._allPatterns.filter(p => p.region === regionFilter);
|
|
76
|
+
|
|
77
|
+
const sizes = { sm: 80, md: 120, lg: 160 };
|
|
78
|
+
const itemSize = sizes[size] || sizes.md;
|
|
79
|
+
|
|
80
|
+
this.shadowRoot.innerHTML = `
|
|
81
|
+
<style>
|
|
82
|
+
:host {
|
|
83
|
+
display: block;
|
|
84
|
+
font-family: 'Inter', system-ui, sans-serif;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.showcase-container {
|
|
88
|
+
padding: 10px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.region-tabs {
|
|
92
|
+
display: flex;
|
|
93
|
+
gap: 8px;
|
|
94
|
+
margin-block-end: 20px;
|
|
95
|
+
flex-wrap: wrap;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.region-tab {
|
|
99
|
+
padding: 8px 16px;
|
|
100
|
+
border: none;
|
|
101
|
+
border-radius: 20px;
|
|
102
|
+
font-family: inherit;
|
|
103
|
+
font-size: 13px;
|
|
104
|
+
font-weight: 500;
|
|
105
|
+
cursor: pointer;
|
|
106
|
+
background: #e9ecef;
|
|
107
|
+
color: #495057;
|
|
108
|
+
transition: all 200ms ease;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.region-tab:hover {
|
|
112
|
+
background: #dee2e6;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.region-tab.active {
|
|
116
|
+
background: linear-gradient(135deg, #1EB53A 0%, #00A3DD 100%);
|
|
117
|
+
color: white;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.pattern-grid {
|
|
121
|
+
display: grid;
|
|
122
|
+
grid-template-columns: repeat(auto-fill, minmax(${itemSize + 40}px, 1fr));
|
|
123
|
+
gap: 16px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.pattern-item {
|
|
127
|
+
background: white;
|
|
128
|
+
border-radius: 12px;
|
|
129
|
+
overflow: hidden;
|
|
130
|
+
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
131
|
+
cursor: pointer;
|
|
132
|
+
transition: all 300ms ease;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.pattern-item:hover {
|
|
136
|
+
transform: scale(1.05);
|
|
137
|
+
box-shadow: 0 8px 25px rgba(0,0,0,0.2);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.pattern-preview {
|
|
141
|
+
inline-size: 100%;
|
|
142
|
+
block-size: ${itemSize}px;
|
|
143
|
+
background-repeat: repeat;
|
|
144
|
+
transition: background-position 10s linear;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.pattern-item:hover .pattern-preview {
|
|
148
|
+
background-position: 50px 50px;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.pattern-info {
|
|
152
|
+
padding: 12px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.pattern-name {
|
|
156
|
+
font-weight: 600;
|
|
157
|
+
font-size: 14px;
|
|
158
|
+
margin-block-end: 4px;
|
|
159
|
+
color: #333;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.pattern-country {
|
|
163
|
+
font-size: 11px;
|
|
164
|
+
color: #888;
|
|
165
|
+
margin-block-end: 6px;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.pattern-meaning {
|
|
169
|
+
font-size: 11px;
|
|
170
|
+
color: #666;
|
|
171
|
+
font-style: italic;
|
|
172
|
+
line-height: 1.4;
|
|
173
|
+
margin-block-end: 6px;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.region-label {
|
|
177
|
+
font-size: 12px;
|
|
178
|
+
padding: 3px 8px;
|
|
179
|
+
border-radius: 10px;
|
|
180
|
+
display: inline-block;
|
|
181
|
+
margin-block-end: 6px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.region-east { background: #d4edda; color: #155724; }
|
|
185
|
+
.region-west { background: #fff3cd; color: #856404; }
|
|
186
|
+
.region-central { background: #e2e3ff; color: #3e2723; }
|
|
187
|
+
.region-south { background: #d1ecf1; color: #0c5460; }
|
|
188
|
+
</style>
|
|
189
|
+
|
|
190
|
+
<div class="showcase-container">
|
|
191
|
+
<div class="region-tabs">
|
|
192
|
+
<button class="region-tab ${regionFilter === 'all' ? 'active' : ''}" data-region="all">All Regions</button>
|
|
193
|
+
<button class="region-tab ${regionFilter === 'east' ? 'active' : ''}" data-region="east">East Africa</button>
|
|
194
|
+
<button class="region-tab ${regionFilter === 'west' ? 'active' : ''}" data-region="west">West Africa</button>
|
|
195
|
+
<button class="region-tab ${regionFilter === 'central' ? 'active' : ''}" data-region="central">Central Africa</button>
|
|
196
|
+
<button class="region-tab ${regionFilter === 'south' ? 'active' : ''}" data-region="south">Southern Africa</button>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<div class="pattern-grid">
|
|
200
|
+
${filtered.map(p => `
|
|
201
|
+
<div class="pattern-item" data-key="${p.key}">
|
|
202
|
+
<div class="pattern-preview" style="background-image: url('${p.fn()}')"></div>
|
|
203
|
+
<div class="pattern-info">
|
|
204
|
+
<span class="region-label region-${p.region}">${p.region.charAt(0).toUpperCase() + p.region.slice(1)}</span>
|
|
205
|
+
<div class="pattern-name">${p.name}</div>
|
|
206
|
+
<div class="pattern-country">${p.country}</div>
|
|
207
|
+
<div class="pattern-meaning">${p.meaning}</div>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
`).join('')}
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
`;
|
|
214
|
+
|
|
215
|
+
// Region filter tabs
|
|
216
|
+
this.shadowRoot.querySelectorAll('.region-tab').forEach(tab => {
|
|
217
|
+
tab.addEventListener('click', () => {
|
|
218
|
+
this.setAttribute('region', tab.dataset.region);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
registerComponent('af-pattern-showcase', PatternShowcase);
|
|
225
|
+
export default PatternShowcase;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AfriCode Progress Bar Component
|
|
3
|
+
*
|
|
4
|
+
* Progress indicators for loading, completion, and metrics.
|
|
5
|
+
* Suitable for: File uploads, Course progress, Health goals, Downloads
|
|
6
|
+
*
|
|
7
|
+
* @module components/progress
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { AfriCodeComponent, registerComponent } from './base.js';
|
|
11
|
+
|
|
12
|
+
export class AfriProgress extends AfriCodeComponent {
|
|
13
|
+
static get observedAttributes() {
|
|
14
|
+
return ['value', 'max', 'theme', 'size', 'label', 'animated'];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
constructor() {
|
|
18
|
+
super();
|
|
19
|
+
this.render();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
attributeChangedCallback() {
|
|
23
|
+
this.render();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
render() {
|
|
27
|
+
const value = parseFloat(this.getAttribute('value')) || 0;
|
|
28
|
+
const max = parseFloat(this.getAttribute('max')) || 100;
|
|
29
|
+
const theme = this.getAttribute('theme') || 'tanzania';
|
|
30
|
+
const size = this.getAttribute('size') || 'md';
|
|
31
|
+
const label = this.getAttribute('label');
|
|
32
|
+
const animated = this.hasAttribute('animated');
|
|
33
|
+
|
|
34
|
+
const percentage = Math.min(100, Math.max(0, (value / max) * 100));
|
|
35
|
+
|
|
36
|
+
const themes = {
|
|
37
|
+
tanzania: { bar: '#1EB53A', track: '#e9ecef' },
|
|
38
|
+
maasai: { bar: '#FF0000', track: '#e9ecef' },
|
|
39
|
+
ndebele: { bar: '#4169E1', track: '#e9ecef' },
|
|
40
|
+
gold: { bar: '#FCD116', track: '#e9ecef' },
|
|
41
|
+
gradient: { bar: 'linear-gradient(90deg, #1EB53A 0%, #FCD116 50%, #00A3DD 100%)', track: '#e9ecef' }
|
|
42
|
+
};
|
|
43
|
+
const t = themes[theme] || themes.tanzania;
|
|
44
|
+
|
|
45
|
+
const sizes = {
|
|
46
|
+
sm: '6px',
|
|
47
|
+
md: '10px',
|
|
48
|
+
lg: '16px'
|
|
49
|
+
};
|
|
50
|
+
const height = sizes[size] || sizes.md;
|
|
51
|
+
|
|
52
|
+
this.shadowRoot.innerHTML = `
|
|
53
|
+
<style>
|
|
54
|
+
:host {
|
|
55
|
+
display: block;
|
|
56
|
+
font-family: 'Inter', system-ui, sans-serif;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.progress-container {
|
|
60
|
+
width: 100%;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.progress-header {
|
|
64
|
+
display: flex;
|
|
65
|
+
justify-content: space-between;
|
|
66
|
+
margin-bottom: 5px;
|
|
67
|
+
font-size: 13px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.progress-label {
|
|
71
|
+
color: #333;
|
|
72
|
+
font-weight: 500;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.progress-value {
|
|
76
|
+
color: #666;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.progress-track {
|
|
80
|
+
width: 100%;
|
|
81
|
+
height: ${height};
|
|
82
|
+
background: ${t.track};
|
|
83
|
+
border-radius: 100px;
|
|
84
|
+
overflow: hidden;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.progress-bar {
|
|
88
|
+
height: 100%;
|
|
89
|
+
width: ${percentage}%;
|
|
90
|
+
background: ${t.bar};
|
|
91
|
+
border-radius: 100px;
|
|
92
|
+
transition: width 500ms ease-out;
|
|
93
|
+
${animated ? `
|
|
94
|
+
background-size: 30px 30px;
|
|
95
|
+
background-image: linear-gradient(
|
|
96
|
+
135deg,
|
|
97
|
+
rgba(255,255,255,0.2) 25%,
|
|
98
|
+
transparent 25%,
|
|
99
|
+
transparent 50%,
|
|
100
|
+
rgba(255,255,255,0.2) 50%,
|
|
101
|
+
rgba(255,255,255,0.2) 75%,
|
|
102
|
+
transparent 75%,
|
|
103
|
+
transparent
|
|
104
|
+
);
|
|
105
|
+
animation: stripes 1s linear infinite;
|
|
106
|
+
` : ''}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@keyframes stripes {
|
|
110
|
+
0% { background-position: 0 0; }
|
|
111
|
+
100% { background-position: 30px 0; }
|
|
112
|
+
}
|
|
113
|
+
</style>
|
|
114
|
+
|
|
115
|
+
<div class="progress-container">
|
|
116
|
+
${label || true ? `
|
|
117
|
+
<div class="progress-header">
|
|
118
|
+
<span class="progress-label">${label || ''}</span>
|
|
119
|
+
<span class="progress-value">${percentage.toFixed(0)}%</span>
|
|
120
|
+
</div>
|
|
121
|
+
` : ''}
|
|
122
|
+
<div class="progress-track">
|
|
123
|
+
<div class="progress-bar" role="progressbar"
|
|
124
|
+
aria-valuenow="${value}"
|
|
125
|
+
aria-valuemin="0"
|
|
126
|
+
aria-valuemax="${max}"></div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
registerComponent('af-progress', AfriProgress);
|
|
134
|
+
export default AfriProgress;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AfriCode React Interoperability Wrapper
|
|
3
|
+
*
|
|
4
|
+
* Mounts a React component tree inside an AfriCode Web Component's Shadow DOM.
|
|
5
|
+
* This allows teams to embed React views within AfriCode pages without replacing
|
|
6
|
+
* the framework's routing, build, or HMR systems.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* import { AfriReact } from 'africode/components/react.js';
|
|
10
|
+
* // or use declaratively after registration:
|
|
11
|
+
* <af-react component="MyReactApp"></af-react>
|
|
12
|
+
*
|
|
13
|
+
* Programmatic mount:
|
|
14
|
+
* const wrapper = document.querySelector('af-react');
|
|
15
|
+
* wrapper.mount(MyReactComponent, { someProp: 'value' });
|
|
16
|
+
*
|
|
17
|
+
* @module components/react
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { AfriCodeComponent, registerComponent } from './base.js';
|
|
21
|
+
|
|
22
|
+
export class AfriReact extends AfriCodeComponent {
|
|
23
|
+
static get observedAttributes() {
|
|
24
|
+
return ['component'];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
constructor() {
|
|
28
|
+
super();
|
|
29
|
+
this._root = null; // React root instance
|
|
30
|
+
this._mountPoint = null; // DOM node React renders into
|
|
31
|
+
this._component = null; // The React component function/class
|
|
32
|
+
this._props = {}; // Props passed to the React component
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
connectedCallback() {
|
|
36
|
+
super.connectedCallback();
|
|
37
|
+
|
|
38
|
+
// Create mount point inside Shadow DOM
|
|
39
|
+
this._mountPoint = document.createElement('div');
|
|
40
|
+
this._mountPoint.setAttribute('data-af-react-root', '');
|
|
41
|
+
this.shadowRoot.appendChild(this._mountPoint);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
disconnectedCallback() {
|
|
45
|
+
// Clean unmount to prevent memory leaks
|
|
46
|
+
this._unmount();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Mount a React component into this wrapper.
|
|
51
|
+
* @param {Function|Object} Component - React component (function or class)
|
|
52
|
+
* @param {Object} [props={}] - Props to pass to the component
|
|
53
|
+
*/
|
|
54
|
+
async mount(Component, props = {}) {
|
|
55
|
+
if (!this._mountPoint) {
|
|
56
|
+
console.error('[AfriCode/React] Cannot mount — component not connected to DOM.');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this._component = Component;
|
|
61
|
+
this._props = props;
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const React = await import('react');
|
|
65
|
+
const ReactDOM = await import('react-dom/client');
|
|
66
|
+
|
|
67
|
+
// Unmount any previous tree
|
|
68
|
+
this._unmount();
|
|
69
|
+
|
|
70
|
+
this._root = ReactDOM.createRoot(this._mountPoint);
|
|
71
|
+
this._root.render(React.createElement(Component, props));
|
|
72
|
+
} catch (err) {
|
|
73
|
+
console.error('[AfriCode/React] Failed to mount React component:', err);
|
|
74
|
+
this._mountPoint.innerHTML = `
|
|
75
|
+
<div style="padding: 12px; border: 1px dashed crimson; border-radius: 6px; color: crimson; font-size: 0.85rem;">
|
|
76
|
+
React mount failed. Ensure <code>react</code> and <code>react-dom</code> are installed.
|
|
77
|
+
</div>
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Update props on the currently mounted React component.
|
|
84
|
+
* @param {Object} nextProps - New props to merge and re-render with
|
|
85
|
+
*/
|
|
86
|
+
async update(nextProps) {
|
|
87
|
+
if (!this._root || !this._component) {
|
|
88
|
+
console.warn('[AfriCode/React] No component mounted — call mount() first.');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
this._props = { ...this._props, ...nextProps };
|
|
92
|
+
|
|
93
|
+
const React = await import('react');
|
|
94
|
+
this._root.render(React.createElement(this._component, this._props));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** @private */
|
|
98
|
+
_unmount() {
|
|
99
|
+
if (this._root) {
|
|
100
|
+
try {
|
|
101
|
+
this._root.unmount();
|
|
102
|
+
} catch (e) {
|
|
103
|
+
// Already unmounted or root was invalid
|
|
104
|
+
}
|
|
105
|
+
this._root = null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
registerComponent('af-react', AfriReact);
|
|
111
|
+
export default AfriReact;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AfriCode Content Section
|
|
3
|
+
* Standardized vertical spacing and containerization.
|
|
4
|
+
*/
|
|
5
|
+
import { AfriCodeComponent, registerComponent, html } from './base.js';
|
|
6
|
+
|
|
7
|
+
export class AfriSection extends AfriCodeComponent {
|
|
8
|
+
render() {
|
|
9
|
+
const variant = this.getAttribute('variant') || 'default';
|
|
10
|
+
const title = this.getAttribute('title');
|
|
11
|
+
|
|
12
|
+
this.shadowRoot.innerHTML = html`
|
|
13
|
+
<style>
|
|
14
|
+
:host { display: block; width: 100%; }
|
|
15
|
+
.section {
|
|
16
|
+
padding: 100px 5%;
|
|
17
|
+
max-width: var(--page-max-width, 1280px);
|
|
18
|
+
margin: 0 auto;
|
|
19
|
+
}
|
|
20
|
+
.section-header {
|
|
21
|
+
text-align: center;
|
|
22
|
+
margin-bottom: 60px;
|
|
23
|
+
}
|
|
24
|
+
.section-header h2 {
|
|
25
|
+
font-size: 3rem;
|
|
26
|
+
font-family: 'Outfit', sans-serif;
|
|
27
|
+
margin: 0;
|
|
28
|
+
color: var(--text-primary);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
:host([variant="muted"]) { background: var(--bg-surface, rgba(30, 41, 59, 0.3)); }
|
|
32
|
+
:host([variant="brand"]) {
|
|
33
|
+
background: linear-gradient(135deg, var(--bg-base) 0%, var(--bg-surface) 100%);
|
|
34
|
+
border-top: 1px solid var(--glass-border);
|
|
35
|
+
border-bottom: 1px solid var(--glass-border);
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
38
|
+
<section class="section">
|
|
39
|
+
${title ? html`
|
|
40
|
+
<div class="section-header">
|
|
41
|
+
<h2>${title}</h2>
|
|
42
|
+
</div>
|
|
43
|
+
` : ''}
|
|
44
|
+
<slot></slot>
|
|
45
|
+
</section>
|
|
46
|
+
`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
connectedCallback() {
|
|
50
|
+
this.render();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
registerComponent('af-section', AfriSection);
|