@herdingbits/trailhead-cloudscape 0.0.1
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 +37 -0
- package/dist/adapter.d.ts +13 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +63 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/shell-app.d.ts +7 -0
- package/dist/shell-app.d.ts.map +1 -0
- package/dist/shell-app.js +142 -0
- package/dist/shell-layout.d.ts +17 -0
- package/dist/shell-layout.d.ts.map +1 -0
- package/dist/shell-layout.js +16 -0
- package/dist/shell.css +154 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @herdingbits/trailhead-cloudscape
|
|
2
|
+
|
|
3
|
+
CloudScape design system adapter for the Trailhead micro-frontend framework.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @herdingbits/trailhead-core @herdingbits/trailhead-cloudscape @cloudscape-design/components @cloudscape-design/global-styles react react-dom
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import React from 'react';
|
|
15
|
+
import { createRoot } from 'react-dom/client';
|
|
16
|
+
import { Trailhead } from '@herdingbits/trailhead-core';
|
|
17
|
+
import { CloudScapeAdapter, ShellApp } from '@herdingbits/trailhead-cloudscape';
|
|
18
|
+
import '@cloudscape-design/global-styles/index.css';
|
|
19
|
+
import '@herdingbits/trailhead-cloudscape/shell.css';
|
|
20
|
+
|
|
21
|
+
const shell = new Trailhead({
|
|
22
|
+
adapter: new CloudScapeAdapter(),
|
|
23
|
+
basePath: '/app',
|
|
24
|
+
apiUrl: 'https://api.example.com'
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const root = createRoot(document.getElementById('app')!);
|
|
28
|
+
root.render(<ShellApp shell={shell} />);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Documentation
|
|
32
|
+
|
|
33
|
+
See the [main Trailhead documentation](https://github.com/herdingbits/trailhead) for more information.
|
|
34
|
+
|
|
35
|
+
## License
|
|
36
|
+
|
|
37
|
+
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CloudScape Design System Adapter
|
|
3
|
+
* Uses CloudScape Flashbar, Modal, and Spinner components
|
|
4
|
+
*/
|
|
5
|
+
import type { DesignSystemAdapter, FeedbackAdapter } from '@herdingbits/trailhead-types/adapters';
|
|
6
|
+
export declare class CloudScapeAdapter implements DesignSystemAdapter {
|
|
7
|
+
name: string;
|
|
8
|
+
version: string;
|
|
9
|
+
feedback: FeedbackAdapter;
|
|
10
|
+
constructor();
|
|
11
|
+
init(basePath: string): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAA4C,MAAM,uCAAuC,CAAC;AAyE5I,qBAAa,iBAAkB,YAAW,mBAAmB;IAC3D,IAAI,SAAgB;IACpB,OAAO,SAAW;IAClB,QAAQ,EAAE,eAAe,CAAC;;IAMpB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG5C"}
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
class CloudScapeFeedbackAdapter {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.flashMessages = [];
|
|
4
|
+
this.busyMessage = '';
|
|
5
|
+
}
|
|
6
|
+
setFlashChangeHandler(handler) {
|
|
7
|
+
this.onFlashChange = handler;
|
|
8
|
+
}
|
|
9
|
+
setBusyChangeHandler(handler) {
|
|
10
|
+
this.onBusyChange = handler;
|
|
11
|
+
}
|
|
12
|
+
showBusy(message) {
|
|
13
|
+
this.busyMessage = message;
|
|
14
|
+
this.onBusyChange?.(message);
|
|
15
|
+
}
|
|
16
|
+
clearBusy() {
|
|
17
|
+
this.busyMessage = '';
|
|
18
|
+
this.onBusyChange?.('');
|
|
19
|
+
}
|
|
20
|
+
showToast(message, variant, duration = 3000) {
|
|
21
|
+
const id = `flash-${Date.now()}`;
|
|
22
|
+
const flash = {
|
|
23
|
+
id,
|
|
24
|
+
type: variant,
|
|
25
|
+
content: message,
|
|
26
|
+
dismissible: true,
|
|
27
|
+
};
|
|
28
|
+
this.flashMessages.push(flash);
|
|
29
|
+
this.onFlashChange?.([...this.flashMessages]);
|
|
30
|
+
if (duration > 0) {
|
|
31
|
+
setTimeout(() => {
|
|
32
|
+
this.flashMessages = this.flashMessages.filter(f => f.id !== id);
|
|
33
|
+
this.onFlashChange?.([...this.flashMessages]);
|
|
34
|
+
}, duration);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
dismissFlash(id) {
|
|
38
|
+
this.flashMessages = this.flashMessages.filter(f => f.id !== id);
|
|
39
|
+
this.onFlashChange?.([...this.flashMessages]);
|
|
40
|
+
}
|
|
41
|
+
async showDialog(config) {
|
|
42
|
+
// Modal dialogs handled by React component
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
const event = new CustomEvent('cloudscape-dialog', {
|
|
45
|
+
detail: { config, resolve }
|
|
46
|
+
});
|
|
47
|
+
window.dispatchEvent(event);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async initAdapter() {
|
|
51
|
+
// No initialization needed
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export class CloudScapeAdapter {
|
|
55
|
+
constructor() {
|
|
56
|
+
this.name = 'cloudscape';
|
|
57
|
+
this.version = '3.0.0';
|
|
58
|
+
this.feedback = new CloudScapeFeedbackAdapter();
|
|
59
|
+
}
|
|
60
|
+
async init(basePath) {
|
|
61
|
+
// CloudScape styles loaded via global-styles package
|
|
62
|
+
}
|
|
63
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @herdingbits/trailhead-cloudscape
|
|
3
|
+
* CloudScape design system adapter for Trailhead
|
|
4
|
+
*/
|
|
5
|
+
export { CloudScapeAdapter } from './adapter.js';
|
|
6
|
+
export { ShellApp } from './shell-app.js';
|
|
7
|
+
export { ShellLayout } from './shell-layout.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Trailhead } from '@herdingbits/trailhead-core';
|
|
2
|
+
interface ShellAppProps {
|
|
3
|
+
shell: Trailhead;
|
|
4
|
+
}
|
|
5
|
+
export declare function ShellApp({ shell }: ShellAppProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=shell-app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-app.d.ts","sourceRoot":"","sources":["../src/shell-app.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAE7D,UAAU,aAAa;IACrB,KAAK,EAAE,SAAS,CAAC;CAClB;AAiBD,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE,aAAa,2CAyMhD"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useRef } from 'react';
|
|
3
|
+
import Flashbar from '@cloudscape-design/components/flashbar';
|
|
4
|
+
import Spinner from '@cloudscape-design/components/spinner';
|
|
5
|
+
import Modal from '@cloudscape-design/components/modal';
|
|
6
|
+
import Box from '@cloudscape-design/components/box';
|
|
7
|
+
import SpaceBetween from '@cloudscape-design/components/space-between';
|
|
8
|
+
import Button from '@cloudscape-design/components/button';
|
|
9
|
+
import { ShellLayout } from './shell-layout.js';
|
|
10
|
+
export function ShellApp({ shell }) {
|
|
11
|
+
const [navigation, setNavigation] = useState([]);
|
|
12
|
+
const [flashMessages, setFlashMessages] = useState([]);
|
|
13
|
+
const [busyMessage, setBusyMessage] = useState('');
|
|
14
|
+
const [dialogState, setDialogState] = useState({
|
|
15
|
+
visible: false,
|
|
16
|
+
message: '',
|
|
17
|
+
buttons: []
|
|
18
|
+
});
|
|
19
|
+
const contentRef = useRef(null);
|
|
20
|
+
// Get current path without basePath
|
|
21
|
+
const getCurrentPath = () => {
|
|
22
|
+
let path = window.location.pathname;
|
|
23
|
+
if (shell.basePath && path.startsWith(shell.basePath)) {
|
|
24
|
+
path = path.substring(shell.basePath.length) || '/';
|
|
25
|
+
}
|
|
26
|
+
return path;
|
|
27
|
+
};
|
|
28
|
+
const [currentPath, setCurrentPath] = useState(getCurrentPath());
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
// Connect adapter to React state
|
|
31
|
+
const adapter = shell.adapter.feedback;
|
|
32
|
+
adapter.setFlashChangeHandler(setFlashMessages);
|
|
33
|
+
adapter.setBusyChangeHandler(setBusyMessage);
|
|
34
|
+
// Listen for dialog events
|
|
35
|
+
const handleDialogEvent = (event) => {
|
|
36
|
+
const { config, resolve } = event.detail;
|
|
37
|
+
setDialogState({
|
|
38
|
+
visible: true,
|
|
39
|
+
title: config.title,
|
|
40
|
+
message: config.message,
|
|
41
|
+
buttons: config.buttons,
|
|
42
|
+
resolve
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
window.addEventListener('cloudscape-dialog', handleDialogEvent);
|
|
46
|
+
// Load navigation
|
|
47
|
+
fetch(`${shell.basePath}/navigation.json`)
|
|
48
|
+
.then(res => res.json())
|
|
49
|
+
.then(data => {
|
|
50
|
+
setNavigation(data);
|
|
51
|
+
})
|
|
52
|
+
.catch(err => console.error('Failed to load navigation:', err));
|
|
53
|
+
return () => {
|
|
54
|
+
window.removeEventListener('cloudscape-dialog', handleDialogEvent);
|
|
55
|
+
};
|
|
56
|
+
}, []);
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
// Handle route when navigation is loaded
|
|
59
|
+
if (navigation.length > 0) {
|
|
60
|
+
handleRoute(currentPath);
|
|
61
|
+
}
|
|
62
|
+
}, [navigation, currentPath]);
|
|
63
|
+
const handleRoute = (path) => {
|
|
64
|
+
// Normalize path by removing trailing slash for matching
|
|
65
|
+
const normalizedPath = path.endsWith('/') && path !== '/' ? path.slice(0, -1) : path;
|
|
66
|
+
const route = navigation.find(item => item.path === normalizedPath);
|
|
67
|
+
if (route && contentRef.current) {
|
|
68
|
+
loadApp(route.app, route.path);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const loadApp = async (appName, appPath) => {
|
|
72
|
+
if (!contentRef.current)
|
|
73
|
+
return;
|
|
74
|
+
contentRef.current.innerHTML = '<div>Loading...</div>';
|
|
75
|
+
try {
|
|
76
|
+
const pluginUrl = `${shell.basePath}${appPath}/app.js`;
|
|
77
|
+
const pluginCss = `${shell.basePath}${appPath}/${appName}.css`;
|
|
78
|
+
// Load CSS
|
|
79
|
+
const link = document.createElement("link");
|
|
80
|
+
link.rel = "stylesheet";
|
|
81
|
+
link.href = pluginCss;
|
|
82
|
+
document.head.appendChild(link);
|
|
83
|
+
// Load JS
|
|
84
|
+
const script = document.createElement("script");
|
|
85
|
+
script.src = pluginUrl;
|
|
86
|
+
script.type = "module";
|
|
87
|
+
script.onload = () => {
|
|
88
|
+
contentRef.current.innerHTML = "";
|
|
89
|
+
if (window.AppMount) {
|
|
90
|
+
window.AppMount(contentRef.current);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
script.onerror = () => {
|
|
94
|
+
contentRef.current.innerHTML = `<div>Failed to load application: ${appName}</div>`;
|
|
95
|
+
};
|
|
96
|
+
document.body.appendChild(script);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
console.error("Failed to load plugin:", error);
|
|
100
|
+
contentRef.current.innerHTML = '<div>Failed to load application</div>';
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const handleNavigate = (path) => {
|
|
104
|
+
// Use full page reload for navigation (no server rewrites needed)
|
|
105
|
+
window.location.href = path;
|
|
106
|
+
};
|
|
107
|
+
const handleDialogButton = (value) => {
|
|
108
|
+
if (dialogState.resolve) {
|
|
109
|
+
dialogState.resolve({ value });
|
|
110
|
+
}
|
|
111
|
+
setDialogState({ ...dialogState, visible: false });
|
|
112
|
+
};
|
|
113
|
+
const handleDialogDismiss = () => {
|
|
114
|
+
if (dialogState.resolve) {
|
|
115
|
+
dialogState.resolve({ value: null });
|
|
116
|
+
}
|
|
117
|
+
setDialogState({ ...dialogState, visible: false });
|
|
118
|
+
};
|
|
119
|
+
return (_jsxs(_Fragment, { children: [flashMessages.length > 0 && (_jsx("div", { style: { position: 'fixed', top: 0, left: 0, right: 0, zIndex: 9999 }, children: _jsx(Flashbar, { items: flashMessages.map(msg => ({
|
|
120
|
+
type: msg.type,
|
|
121
|
+
content: msg.content,
|
|
122
|
+
dismissible: msg.dismissible,
|
|
123
|
+
onDismiss: () => shell.adapter.feedback.dismissFlash(msg.id),
|
|
124
|
+
id: msg.id,
|
|
125
|
+
})) }) })), busyMessage && (_jsx("div", { style: {
|
|
126
|
+
position: 'fixed',
|
|
127
|
+
top: 0,
|
|
128
|
+
left: 0,
|
|
129
|
+
right: 0,
|
|
130
|
+
bottom: 0,
|
|
131
|
+
background: 'rgba(0, 0, 0, 0.5)',
|
|
132
|
+
display: 'flex',
|
|
133
|
+
alignItems: 'center',
|
|
134
|
+
justifyContent: 'center',
|
|
135
|
+
zIndex: 10000,
|
|
136
|
+
}, children: _jsxs("div", { style: {
|
|
137
|
+
background: 'white',
|
|
138
|
+
padding: '24px',
|
|
139
|
+
borderRadius: '8px',
|
|
140
|
+
textAlign: 'center',
|
|
141
|
+
}, children: [_jsx(Spinner, { size: "large" }), _jsx("div", { style: { marginTop: '16px' }, children: busyMessage })] }) })), _jsx(Modal, { visible: dialogState.visible, onDismiss: handleDialogDismiss, header: dialogState.title, footer: _jsx(Box, { float: "right", children: _jsx(SpaceBetween, { direction: "horizontal", size: "xs", children: dialogState.buttons.map((btn, idx) => (_jsx(Button, { variant: btn.variant === 'primary' ? 'primary' : 'normal', onClick: () => handleDialogButton(btn.value), children: btn.label }, idx))) }) }), children: dialogState.message }), _jsx(ShellLayout, { navigation: navigation, currentPath: currentPath, basePath: shell.basePath, onNavigate: handleNavigate, children: _jsx("div", { ref: contentRef }) })] }));
|
|
142
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface NavigationItem {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
path: string;
|
|
6
|
+
icon?: string;
|
|
7
|
+
}
|
|
8
|
+
interface ShellLayoutProps {
|
|
9
|
+
navigation: NavigationItem[];
|
|
10
|
+
currentPath: string;
|
|
11
|
+
basePath: string;
|
|
12
|
+
onNavigate: (path: string) => void;
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
}
|
|
15
|
+
export declare function ShellLayout({ navigation, currentPath, basePath, onNavigate, children }: ShellLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=shell-layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-layout.d.ts","sourceRoot":"","sources":["../src/shell-layout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAMnD,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,gBAAgB;IACxB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,WAAW,CAAC,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,gBAAgB,2CA2BxG"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import AppLayout from '@cloudscape-design/components/app-layout';
|
|
4
|
+
import SideNavigation from '@cloudscape-design/components/side-navigation';
|
|
5
|
+
export function ShellLayout({ navigation, currentPath, basePath, onNavigate, children }) {
|
|
6
|
+
const [navigationOpen, setNavigationOpen] = useState(true);
|
|
7
|
+
const navItems = navigation.map(item => ({
|
|
8
|
+
type: 'link',
|
|
9
|
+
text: item.label,
|
|
10
|
+
href: basePath + item.path + '/', // Add trailing slash for directory routing
|
|
11
|
+
}));
|
|
12
|
+
return (_jsx(AppLayout, { navigationOpen: navigationOpen, onNavigationChange: ({ detail }) => setNavigationOpen(detail.open), navigation: _jsx(SideNavigation, { activeHref: currentPath, items: navItems, onFollow: (event) => {
|
|
13
|
+
event.preventDefault();
|
|
14
|
+
onNavigate(event.detail.href);
|
|
15
|
+
} }), content: children, toolsHide: true }));
|
|
16
|
+
}
|
package/dist/shell.css
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CloudScape Shell Styles
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
* {
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
body {
|
|
10
|
+
margin: 0;
|
|
11
|
+
font-family: "Amazon Ember", "Helvetica Neue", Roboto, Arial, sans-serif;
|
|
12
|
+
background: #f2f3f3;
|
|
13
|
+
color: #000716;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Shell Layout */
|
|
17
|
+
#shell-sidebar {
|
|
18
|
+
position: fixed;
|
|
19
|
+
left: 0;
|
|
20
|
+
top: 0;
|
|
21
|
+
bottom: 0;
|
|
22
|
+
width: 280px;
|
|
23
|
+
background: #232f3e;
|
|
24
|
+
color: white;
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
z-index: 100;
|
|
28
|
+
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#shell-header {
|
|
32
|
+
padding: 20px;
|
|
33
|
+
border-bottom: 1px solid #414d5c;
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
justify-content: space-between;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#shell-title {
|
|
40
|
+
margin: 0;
|
|
41
|
+
font-size: 18px;
|
|
42
|
+
font-weight: 700;
|
|
43
|
+
color: white;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#shell-collapse {
|
|
47
|
+
background: transparent;
|
|
48
|
+
border: none;
|
|
49
|
+
color: white;
|
|
50
|
+
cursor: pointer;
|
|
51
|
+
padding: 8px;
|
|
52
|
+
border-radius: 4px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#shell-collapse:hover {
|
|
56
|
+
background: rgba(255, 255, 255, 0.1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Navigation */
|
|
60
|
+
#shell-navigation {
|
|
61
|
+
flex: 1;
|
|
62
|
+
overflow-y: auto;
|
|
63
|
+
padding: 8px 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.shell-nav-item {
|
|
67
|
+
display: flex;
|
|
68
|
+
align-items: center;
|
|
69
|
+
padding: 12px 20px;
|
|
70
|
+
color: #aab7b8;
|
|
71
|
+
text-decoration: none;
|
|
72
|
+
transition: all 0.2s;
|
|
73
|
+
border-left: 4px solid transparent;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.shell-nav-item:hover {
|
|
77
|
+
background: rgba(255, 255, 255, 0.05);
|
|
78
|
+
color: white;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.shell-nav-item-active {
|
|
82
|
+
background: rgba(255, 255, 255, 0.1);
|
|
83
|
+
color: white;
|
|
84
|
+
border-left-color: #0972d3;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.shell-icon {
|
|
88
|
+
margin-right: 12px;
|
|
89
|
+
font-size: 18px;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.shell-nav-label {
|
|
93
|
+
font-size: 14px;
|
|
94
|
+
font-weight: 500;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Main Content */
|
|
98
|
+
#shell-main {
|
|
99
|
+
margin-left: 280px;
|
|
100
|
+
min-height: 100vh;
|
|
101
|
+
background: #f2f3f3;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
#shell-content {
|
|
105
|
+
padding: 24px;
|
|
106
|
+
max-width: 1600px;
|
|
107
|
+
margin: 0 auto;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* Loading State */
|
|
111
|
+
.shell-loading {
|
|
112
|
+
display: flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
justify-content: center;
|
|
115
|
+
min-height: 400px;
|
|
116
|
+
color: #545b64;
|
|
117
|
+
font-size: 16px;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* Error State */
|
|
121
|
+
.shell-error {
|
|
122
|
+
background: white;
|
|
123
|
+
border: 1px solid #e9ebed;
|
|
124
|
+
border-radius: 8px;
|
|
125
|
+
padding: 24px;
|
|
126
|
+
margin: 24px;
|
|
127
|
+
color: #d13212;
|
|
128
|
+
font-size: 14px;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* Responsive */
|
|
132
|
+
@media (max-width: 768px) {
|
|
133
|
+
#shell-sidebar {
|
|
134
|
+
width: 60px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#shell-title,
|
|
138
|
+
.shell-nav-label {
|
|
139
|
+
display: none;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
#shell-main {
|
|
143
|
+
margin-left: 60px;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.shell-nav-item {
|
|
147
|
+
justify-content: center;
|
|
148
|
+
padding: 12px;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.shell-icon {
|
|
152
|
+
margin-right: 0;
|
|
153
|
+
}
|
|
154
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@herdingbits/trailhead-cloudscape",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "CloudScape design system adapter for Trailhead micro-frontend framework",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./shell.css": "./dist/shell.css"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"clean": "rm -rf dist",
|
|
21
|
+
"build": "npm run clean && tsc && cp src/shell.css dist/"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@herdingbits/trailhead-core": "^0.0.3",
|
|
25
|
+
"@cloudscape-design/components": "^3.0.0",
|
|
26
|
+
"@cloudscape-design/global-styles": "^1.0.0",
|
|
27
|
+
"react": "^18.0.0",
|
|
28
|
+
"react-dom": "^18.0.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@herdingbits/trailhead-types": "^0.0.3",
|
|
32
|
+
"@cloudscape-design/components": "^3.0.1194",
|
|
33
|
+
"@cloudscape-design/global-styles": "^1.0.50",
|
|
34
|
+
"@types/react": "^19.2.13",
|
|
35
|
+
"@types/react-dom": "^19.2.3",
|
|
36
|
+
"react": "^19.2.4",
|
|
37
|
+
"react-dom": "^19.2.4",
|
|
38
|
+
"typescript": "^5.9.3"
|
|
39
|
+
},
|
|
40
|
+
"keywords": [
|
|
41
|
+
"micro-frontend",
|
|
42
|
+
"cloudscape",
|
|
43
|
+
"adapter",
|
|
44
|
+
"trailhead",
|
|
45
|
+
"react"
|
|
46
|
+
],
|
|
47
|
+
"author": "HerdingBits",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "https://github.com/herdingbits/trailhead.git",
|
|
55
|
+
"directory": "packages/cloudscape"
|
|
56
|
+
}
|
|
57
|
+
}
|