@herdingbits/trailhead-webawesome 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 ADDED
@@ -0,0 +1,65 @@
1
+ # @herdingbits/trailhead-shoelace
2
+
3
+ Shoelace web components adapter for Trailhead. Vanilla TypeScript, no React required.
4
+
5
+ ## What is this?
6
+
7
+ This package provides a Trailhead adapter for the [Shoelace](https://shoelace.style/) web component library. It handles:
8
+ - Loading and configuring Shoelace components
9
+ - Rendering toasts, dialogs, and busy overlays
10
+ - Integrating Shoelace with the Trailhead shell
11
+ - **Vanilla TypeScript implementation** - no framework required
12
+
13
+ **Perfect for teams that want to stay close to HTML/CSS/JavaScript** without React complexity.
14
+
15
+ ## Key Features
16
+
17
+ - **Framework Agnostic**: Works with any SPA framework (or no framework)
18
+ - **Web Components**: Shoelace components work everywhere
19
+ - **Lightweight**: No React, no virtual DOM, just the browser
20
+ - **Simple**: Straightforward DOM manipulation and event handling
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ npm install @herdingbits/trailhead-core @herdingbits/trailhead-shoelace @shoelace-style/shoelace
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ```typescript
31
+ import { Trailhead } from '@herdingbits/trailhead-core';
32
+ import { ShoelaceAdapter, ShellApp } from '@herdingbits/trailhead-shoelace';
33
+ import '@herdingbits/trailhead-shoelace/shell.css';
34
+
35
+ const shell = new Trailhead({
36
+ adapter: new ShoelaceAdapter(),
37
+ appBasePath: '/app',
38
+ apiUrl: 'https://api.example.com'
39
+ });
40
+
41
+ ShellApp.mount(shell);
42
+ ```
43
+
44
+ By default, Shoelace is served from `${shellUrl}/shoelace`. To load from a CDN instead:
45
+
46
+ ```typescript
47
+ const shell = new Trailhead({
48
+ adapter: new ShoelaceAdapter({ shoelaceUrl: 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.15.0/cdn' }),
49
+ appBasePath: '/app',
50
+ });
51
+ ```
52
+
53
+ ## What's Included
54
+
55
+ - **ShoelaceAdapter** - Implements the Trailhead adapter interface
56
+ - **ShellApp** - Minimal mounting wrapper for API consistency
57
+ - **shell.css** - Base styles for the shell UI
58
+
59
+ ## Documentation
60
+
61
+ See the [main Trailhead documentation](https://github.com/quicken/trailhead) for more information.
62
+
63
+ ## License
64
+
65
+ MIT
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Web Awesome Design System Adapter
3
+ */
4
+ import type { DesignSystemAdapter, FeedbackAdapter } from '@herdingbits/trailhead-types/adapters';
5
+ export interface WebAwesomeAdapterConfig {
6
+ /** URL where Web Awesome is hosted (CDN or local path). Defaults to `${shellUrl}/webawesome`. */
7
+ webAwesomeUrl?: string;
8
+ }
9
+ export declare class WebAwesomeAdapter implements DesignSystemAdapter {
10
+ name: string;
11
+ version: string;
12
+ feedback: FeedbackAdapter;
13
+ private readonly config?;
14
+ constructor(config?: WebAwesomeAdapterConfig);
15
+ init(shellUrl: string): Promise<void>;
16
+ }
17
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAA4C,MAAM,uCAAuC,CAAC;AA2F5I,MAAM,WAAW,uBAAuB;IACtC,iGAAiG;IACjG,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,iBAAkB,YAAW,mBAAmB;IAC3D,IAAI,SAAgB;IACpB,OAAO,SAAW;IAClB,QAAQ,EAAE,eAAe,CAAC;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAA0B;gBAEtC,MAAM,CAAC,EAAE,uBAAuB;IAKtC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAkB5C"}
@@ -0,0 +1,102 @@
1
+ class WebAwesomeFeedbackAdapter {
2
+ constructor() {
3
+ this.busyOverlay = null;
4
+ this.toastContainer = null;
5
+ }
6
+ showBusy(message) {
7
+ if (!this.busyOverlay) {
8
+ this.busyOverlay = document.createElement("div");
9
+ this.busyOverlay.id = "shell-busy-overlay";
10
+ this.busyOverlay.innerHTML = `
11
+ <div class="shell-busy-content">
12
+ <div class="shell-spinner"></div>
13
+ <div class="shell-busy-message"></div>
14
+ </div>
15
+ `;
16
+ document.body.appendChild(this.busyOverlay);
17
+ }
18
+ const messageEl = this.busyOverlay.querySelector(".shell-busy-message");
19
+ if (messageEl) {
20
+ messageEl.textContent = message;
21
+ }
22
+ this.busyOverlay.style.display = "flex";
23
+ }
24
+ clearBusy() {
25
+ if (this.busyOverlay) {
26
+ this.busyOverlay.style.display = "none";
27
+ }
28
+ }
29
+ showToast(message, variant, duration = 3000) {
30
+ if (!this.toastContainer) {
31
+ this.toastContainer = document.createElement("div");
32
+ this.toastContainer.id = "shell-toast-container";
33
+ document.body.appendChild(this.toastContainer);
34
+ }
35
+ const toast = document.createElement("div");
36
+ toast.className = `shell-toast shell-toast-${variant}`;
37
+ toast.textContent = message;
38
+ this.toastContainer.appendChild(toast);
39
+ setTimeout(() => toast.classList.add("shell-toast-show"), 10);
40
+ setTimeout(() => {
41
+ toast.classList.remove("shell-toast-show");
42
+ setTimeout(() => toast.remove(), 300);
43
+ }, duration);
44
+ }
45
+ showDialog(config) {
46
+ return new Promise((resolve) => {
47
+ const dialog = document.createElement("div");
48
+ dialog.className = "shell-dialog-overlay";
49
+ dialog.innerHTML = `
50
+ <div class="shell-dialog">
51
+ ${config.title ? `<div class="shell-dialog-title">${config.title}</div>` : ''}
52
+ <div class="shell-dialog-message">${config.message}</div>
53
+ <div class="shell-dialog-buttons">
54
+ ${config.buttons
55
+ .map((btn) => `<button class="shell-btn shell-btn-${btn.variant || "default"}" data-value="${btn.value}">${btn.label}</button>`)
56
+ .join("")}
57
+ </div>
58
+ </div>
59
+ `;
60
+ dialog.querySelectorAll("button").forEach((btn) => {
61
+ btn.onclick = () => {
62
+ const value = btn.getAttribute("data-value");
63
+ dialog.remove();
64
+ resolve({ value });
65
+ };
66
+ });
67
+ // Click outside to cancel
68
+ dialog.onclick = (e) => {
69
+ if (e.target === dialog) {
70
+ dialog.remove();
71
+ resolve({ value: null });
72
+ }
73
+ };
74
+ document.body.appendChild(dialog);
75
+ });
76
+ }
77
+ }
78
+ export class WebAwesomeAdapter {
79
+ constructor(config) {
80
+ this.name = "webawesome";
81
+ this.version = "1.0.0";
82
+ this.config = config;
83
+ this.feedback = new WebAwesomeFeedbackAdapter();
84
+ }
85
+ async init(shellUrl) {
86
+ try {
87
+ const waPath = this.config?.webAwesomeUrl ?? `${shellUrl}/webawesome`;
88
+ // Inject theme CSS dynamically so it resolves correctly regardless of where the page is served from
89
+ const link = document.createElement("link");
90
+ link.rel = "stylesheet";
91
+ link.href = `${waPath}/styles/themes/default.css`;
92
+ document.head.appendChild(link);
93
+ const { setBasePath } = await import(/* @vite-ignore */ `${waPath}/webawesome.js`);
94
+ setBasePath(waPath);
95
+ await import(/* @vite-ignore */ `${waPath}/webawesome.loader.js`);
96
+ }
97
+ catch (error) {
98
+ console.error("Failed to initialize Web Awesome:", error);
99
+ throw error;
100
+ }
101
+ }
102
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @herdingbits/trailhead-webawesome
3
+ * Web Awesome design system adapter for Trailhead
4
+ */
5
+ export { WebAwesomeAdapter } from './adapter.js';
6
+ export type { WebAwesomeAdapterConfig } from './adapter.js';
7
+ export { ShellApp } from './shell-app.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,YAAY,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @herdingbits/trailhead-webawesome
3
+ * Web Awesome design system adapter for Trailhead
4
+ */
5
+ export { WebAwesomeAdapter } from './adapter.js';
6
+ export { ShellApp } from './shell-app.js';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Shoelace Shell App
3
+ * Minimal wrapper for consistency with other adapters
4
+ */
5
+ import type { Trailhead } from '@herdingbits/trailhead-core';
6
+ export declare class ShellApp {
7
+ /**
8
+ * Mount the shell (no-op for Shoelace since adapter handles everything)
9
+ */
10
+ static mount(shell: Trailhead): void;
11
+ }
12
+ //# sourceMappingURL=shell-app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-app.d.ts","sourceRoot":"","sources":["../src/shell-app.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAE7D,qBAAa,QAAQ;IACnB;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;CAKrC"}
@@ -0,0 +1,10 @@
1
+ export class ShellApp {
2
+ /**
3
+ * Mount the shell (no-op for Shoelace since adapter handles everything)
4
+ */
5
+ static mount(shell) {
6
+ // Shoelace adapter handles all UI via web components
7
+ // This is just a consistent API surface
8
+ console.log('[Trailhead] Shoelace shell mounted');
9
+ }
10
+ }
package/dist/shell.css ADDED
@@ -0,0 +1,287 @@
1
+ /**
2
+ * Shell Styles
3
+ */
4
+
5
+ /* Reset */
6
+ * {
7
+ margin: 0;
8
+ padding: 0;
9
+ box-sizing: border-box;
10
+ }
11
+
12
+ body {
13
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
14
+ -webkit-font-smoothing: antialiased;
15
+ -moz-osx-font-smoothing: grayscale;
16
+ display: flex;
17
+ height: 100vh;
18
+ overflow: hidden;
19
+ }
20
+
21
+ /* Sidebar */
22
+ #shell-sidebar {
23
+ width: 240px;
24
+ background-color: #1e293b;
25
+ color: white;
26
+ display: flex;
27
+ flex-direction: column;
28
+ transition: width 0.3s;
29
+ }
30
+
31
+ #shell-sidebar.collapsed {
32
+ width: 60px;
33
+ }
34
+
35
+ #shell-header {
36
+ padding: 1rem;
37
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: space-between;
41
+ }
42
+
43
+ #shell-title {
44
+ font-size: 1.25rem;
45
+ font-weight: 600;
46
+ }
47
+
48
+ #shell-sidebar.collapsed #shell-title {
49
+ display: none;
50
+ }
51
+
52
+ #shell-collapse {
53
+ background: none;
54
+ border: none;
55
+ color: white;
56
+ cursor: pointer;
57
+ padding: 0.5rem;
58
+ }
59
+
60
+ /* Navigation */
61
+ #shell-navigation {
62
+ flex: 1;
63
+ padding: 1rem 0;
64
+ overflow-y: auto;
65
+ }
66
+
67
+ .shell-nav-item {
68
+ display: flex;
69
+ align-items: center;
70
+ gap: 0.75rem;
71
+ padding: 0.75rem 1rem;
72
+ color: white;
73
+ text-decoration: none;
74
+ transition: background-color 0.2s;
75
+ border-left: 3px solid transparent;
76
+ }
77
+
78
+ .shell-nav-item:hover {
79
+ background-color: rgba(255, 255, 255, 0.1);
80
+ }
81
+
82
+ .shell-nav-item-active {
83
+ background-color: rgba(59, 130, 246, 0.2);
84
+ border-left-color: #3b82f6;
85
+ }
86
+
87
+ #shell-sidebar.collapsed .shell-nav-label {
88
+ display: none;
89
+ }
90
+
91
+ .shell-icon {
92
+ font-size: 1.25rem;
93
+ }
94
+
95
+ /* Main content */
96
+ #shell-main {
97
+ flex: 1;
98
+ background-color: #f8fafc;
99
+ overflow: auto;
100
+ }
101
+
102
+ #shell-content {
103
+ height: 100%;
104
+ }
105
+
106
+ /* Loading state */
107
+ .shell-loading {
108
+ display: flex;
109
+ align-items: center;
110
+ justify-content: center;
111
+ height: 100%;
112
+ font-size: 1.25rem;
113
+ color: #64748b;
114
+ }
115
+
116
+ .shell-error {
117
+ display: flex;
118
+ align-items: center;
119
+ justify-content: center;
120
+ height: 100%;
121
+ font-size: 1.25rem;
122
+ color: #ef4444;
123
+ }
124
+
125
+ /* Busy overlay */
126
+ #shell-busy-overlay {
127
+ position: fixed;
128
+ top: 0;
129
+ left: 0;
130
+ right: 0;
131
+ bottom: 0;
132
+ background-color: rgba(0, 0, 0, 0.5);
133
+ display: none;
134
+ align-items: center;
135
+ justify-content: center;
136
+ z-index: 9999;
137
+ }
138
+
139
+ .shell-busy-content {
140
+ background: white;
141
+ padding: 2rem;
142
+ border-radius: 8px;
143
+ text-align: center;
144
+ min-width: 200px;
145
+ }
146
+
147
+ .shell-spinner {
148
+ width: 40px;
149
+ height: 40px;
150
+ border: 4px solid #e5e7eb;
151
+ border-top-color: #3b82f6;
152
+ border-radius: 50%;
153
+ animation: shell-spin 0.8s linear infinite;
154
+ margin: 0 auto 1rem;
155
+ }
156
+
157
+ @keyframes shell-spin {
158
+ to { transform: rotate(360deg); }
159
+ }
160
+
161
+ .shell-busy-message {
162
+ color: #1e293b;
163
+ font-size: 1rem;
164
+ }
165
+
166
+ /* Toast notifications */
167
+ #shell-toast-container {
168
+ position: fixed;
169
+ top: 1rem;
170
+ right: 1rem;
171
+ z-index: 10000;
172
+ display: flex;
173
+ flex-direction: column;
174
+ gap: 0.5rem;
175
+ }
176
+
177
+ .shell-toast {
178
+ padding: 1rem 1.5rem;
179
+ border-radius: 6px;
180
+ color: white;
181
+ font-size: 0.875rem;
182
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
183
+ opacity: 0;
184
+ transform: translateX(100%);
185
+ transition: all 0.3s;
186
+ max-width: 400px;
187
+ }
188
+
189
+ .shell-toast-show {
190
+ opacity: 1;
191
+ transform: translateX(0);
192
+ }
193
+
194
+ .shell-toast-success {
195
+ background-color: #10b981;
196
+ }
197
+
198
+ .shell-toast-error {
199
+ background-color: #ef4444;
200
+ }
201
+
202
+ .shell-toast-warning {
203
+ background-color: #f59e0b;
204
+ }
205
+
206
+ .shell-toast-info {
207
+ background-color: #3b82f6;
208
+ }
209
+
210
+ /* Dialog */
211
+ .shell-dialog-overlay {
212
+ position: fixed;
213
+ top: 0;
214
+ left: 0;
215
+ right: 0;
216
+ bottom: 0;
217
+ background-color: rgba(0, 0, 0, 0.5);
218
+ display: flex;
219
+ align-items: center;
220
+ justify-content: center;
221
+ z-index: 10001;
222
+ }
223
+
224
+ .shell-dialog {
225
+ background: white;
226
+ border-radius: 8px;
227
+ padding: 1.5rem;
228
+ min-width: 400px;
229
+ max-width: 600px;
230
+ box-shadow: 0 20px 25px rgba(0, 0, 0, 0.15);
231
+ }
232
+
233
+ .shell-dialog-title {
234
+ font-size: 1.25rem;
235
+ font-weight: 600;
236
+ color: #1e293b;
237
+ margin-bottom: 1rem;
238
+ }
239
+
240
+ .shell-dialog-message {
241
+ color: #64748b;
242
+ margin-bottom: 1.5rem;
243
+ line-height: 1.5;
244
+ }
245
+
246
+ .shell-dialog-buttons {
247
+ display: flex;
248
+ gap: 0.75rem;
249
+ justify-content: flex-end;
250
+ }
251
+
252
+ .shell-btn {
253
+ padding: 0.5rem 1rem;
254
+ border: none;
255
+ border-radius: 6px;
256
+ font-size: 0.875rem;
257
+ font-weight: 500;
258
+ cursor: pointer;
259
+ transition: all 0.2s;
260
+ }
261
+
262
+ .shell-btn-primary {
263
+ background-color: #3b82f6;
264
+ color: white;
265
+ }
266
+
267
+ .shell-btn-primary:hover {
268
+ background-color: #2563eb;
269
+ }
270
+
271
+ .shell-btn-secondary {
272
+ background-color: #e5e7eb;
273
+ color: #1e293b;
274
+ }
275
+
276
+ .shell-btn-secondary:hover {
277
+ background-color: #d1d5db;
278
+ }
279
+
280
+ .shell-btn-default {
281
+ background-color: #f3f4f6;
282
+ color: #1e293b;
283
+ }
284
+
285
+ .shell-btn-default:hover {
286
+ background-color: #e5e7eb;
287
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@herdingbits/trailhead-webawesome",
3
+ "version": "0.1.0",
4
+ "description": "Web Awesome web components adapter for Trailhead. Vanilla TypeScript, no React required.",
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.18",
25
+ "@awesome.me/webawesome": "^3.6.0"
26
+ },
27
+ "devDependencies": {
28
+ "@herdingbits/trailhead-types": "^0.0.14",
29
+ "@awesome.me/webawesome": "^3.6.0",
30
+ "typescript": "^6.0.3"
31
+ },
32
+ "keywords": [
33
+ "micro-frontend",
34
+ "webawesome",
35
+ "web-awesome",
36
+ "adapter",
37
+ "trailhead"
38
+ ],
39
+ "author": "HerdingBits",
40
+ "license": "MIT",
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "https://github.com/quicken/trailhead.git",
47
+ "directory": "packages/webawesome"
48
+ }
49
+ }