@sparkstudio/common-ui 1.0.20 → 1.0.22

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/dist/index.cjs CHANGED
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  LayoutWrapper: () => LayoutWrapper,
35
35
  Modal: () => Modal,
36
36
  ModalProvider: () => ModalProvider,
37
+ NavToggleButton: () => NavToggleButton,
37
38
  Navigation: () => Navigation,
38
39
  NavigationItem: () => NavigationItem,
39
40
  Notifications: () => Notifications,
@@ -119,6 +120,7 @@ function Button({
119
120
  buttonClassName = "btn btn-primary btn-lg",
120
121
  type = "button",
121
122
  disabled = false,
123
+ variant,
122
124
  onAction,
123
125
  onClick,
124
126
  onDone,
@@ -148,11 +150,12 @@ function Button({
148
150
  onFinally?.();
149
151
  }
150
152
  };
153
+ const baseClass = variant ? `btn btn-${variant}` : buttonClassName;
151
154
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
152
155
  "button",
153
156
  {
154
157
  type,
155
- className: `${buttonClassName} ${loading ? "async-btn--loading" : ""}`,
158
+ className: `${baseClass} ${loading ? "async-btn--loading" : ""}`,
156
159
  onClick: handleClick,
157
160
  disabled: disabled || loading,
158
161
  children: loading ? renderLoading?.() ?? loadingChildren ?? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
@@ -219,10 +222,40 @@ function NpmBadge({ packageName, packageLabel, packageUrl, color = "black", heig
219
222
 
220
223
  // src/components/navigation/Navigation.tsx
221
224
  var import_jsx_runtime10 = require("react/jsx-runtime");
222
- function Navigation({ title = "Navigation", children }) {
223
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("nav", { className: "col-auto p-3 h-100 d-flex flex-column", children: [
224
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h6", { className: "text-muted text-uppercase", children: title }),
225
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("ul", { className: "nav nav-pills flex-column gap-2 mt-3", children })
225
+ function Navigation({
226
+ title = "Navigation",
227
+ children,
228
+ isOpen,
229
+ onClose
230
+ }) {
231
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
232
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("nav", { className: "col-auto p-3 h-100 d-none d-md-flex flex-column", children: [
233
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h6", { className: "text-muted text-uppercase", children: title }),
234
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("ul", { className: "nav nav-pills flex-column gap-2 mt-3", children })
235
+ ] }),
236
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
237
+ "div",
238
+ {
239
+ className: `offcanvas offcanvas-start d-md-none ${isOpen ? "show" : ""}`,
240
+ style: { visibility: isOpen ? "visible" : "hidden" },
241
+ tabIndex: -1,
242
+ "aria-hidden": isOpen ? "false" : "true",
243
+ children: [
244
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "offcanvas-header", children: [
245
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h5", { className: "offcanvas-title", children: title }),
246
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { className: "btn-close", onClick: onClose })
247
+ ] }),
248
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "offcanvas-body", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("ul", { className: "nav nav-pills flex-column gap-2 mt-2", children }) })
249
+ ]
250
+ }
251
+ ),
252
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
253
+ "div",
254
+ {
255
+ className: "offcanvas-backdrop fade show d-md-none",
256
+ onClick: onClose
257
+ }
258
+ )
226
259
  ] });
227
260
  }
228
261
 
@@ -244,9 +277,30 @@ function NavigationItem({ icon, title, type, active, onClick }) {
244
277
  ) });
245
278
  }
246
279
 
280
+ // src/components/navigation/NavToggleButton.tsx
281
+ var import_jsx_runtime12 = require("react/jsx-runtime");
282
+ var NavToggleButton = ({
283
+ onClick,
284
+ label = "Menu",
285
+ color = "secondary"
286
+ }) => {
287
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "d-md-none position-absolute top-0 end-0 m-2", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
288
+ Button,
289
+ {
290
+ variant: color,
291
+ buttonClassName: "btn btn-secondary",
292
+ onClick: () => onClick(),
293
+ children: [
294
+ "\u2630 ",
295
+ label
296
+ ]
297
+ }
298
+ ) });
299
+ };
300
+
247
301
  // src/components/notifications/Notifications.tsx
248
302
  var import_react2 = require("react");
249
- var import_jsx_runtime12 = require("react/jsx-runtime");
303
+ var import_jsx_runtime13 = require("react/jsx-runtime");
250
304
  function Notifications({ notifications, onClose }) {
251
305
  (0, import_react2.useEffect)(() => {
252
306
  notifications.forEach((n) => {
@@ -255,19 +309,19 @@ function Notifications({ notifications, onClose }) {
255
309
  return () => clearTimeout(timer);
256
310
  });
257
311
  }, [notifications, onClose]);
258
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
312
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
259
313
  "div",
260
314
  {
261
315
  className: "position-fixed top-0 end-0",
262
316
  style: { zIndex: 1080, width: 340 },
263
- children: notifications.map((n) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
317
+ children: notifications.map((n) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
264
318
  "div",
265
319
  {
266
320
  className: `alert alert-${n.type ?? "info"} alert-dismissible fade show shadow-sm mb-2`,
267
321
  role: "alert",
268
322
  children: [
269
323
  n.message,
270
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
324
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
271
325
  "button",
272
326
  {
273
327
  type: "button",
@@ -286,7 +340,7 @@ function Notifications({ notifications, onClose }) {
286
340
 
287
341
  // src/components/notifications/NotificationsProvider.tsx
288
342
  var import_react3 = require("react");
289
- var import_jsx_runtime13 = require("react/jsx-runtime");
343
+ var import_jsx_runtime14 = require("react/jsx-runtime");
290
344
  var NotificationsContext = (0, import_react3.createContext)(
291
345
  void 0
292
346
  );
@@ -318,20 +372,20 @@ function NotificationsProvider({ children }) {
318
372
  timers.forEach((t) => t && clearTimeout(t));
319
373
  };
320
374
  }, [notifications, close]);
321
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(NotificationsContext.Provider, { value: { push }, children: [
322
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
375
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(NotificationsContext.Provider, { value: { push }, children: [
376
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
323
377
  "div",
324
378
  {
325
379
  className: "position-fixed top-0 end-0 p-3",
326
380
  style: { zIndex: 1080, width: 340 },
327
- children: notifications.map((n) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
381
+ children: notifications.map((n) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
328
382
  "div",
329
383
  {
330
384
  className: `alert alert-${n.type ?? "info"} alert-dismissible fade show shadow-sm mb-2`,
331
385
  role: "alert",
332
386
  children: [
333
387
  n.message,
334
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
388
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
335
389
  "button",
336
390
  {
337
391
  type: "button",
@@ -358,34 +412,34 @@ function UseNotifications() {
358
412
  }
359
413
 
360
414
  // src/components/views/AppsView.tsx
361
- var import_jsx_runtime14 = require("react/jsx-runtime");
415
+ var import_jsx_runtime15 = require("react/jsx-runtime");
362
416
  var AppsView = ({ apps, title, onAppClick }) => {
363
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
364
- title && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h5", { className: "mb-3", children: title }),
365
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "row", children: apps.map((app) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(AppTile, { app, onClick: onAppClick }, app.id)) })
417
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
418
+ title && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h5", { className: "mb-3", children: title }),
419
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "row", children: apps.map((app) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(AppTile, { app, onClick: onAppClick }, app.id)) })
366
420
  ] });
367
421
  };
368
422
 
369
423
  // src/components/wrappers/ContentContainer.tsx
370
- var import_jsx_runtime15 = require("react/jsx-runtime");
424
+ var import_jsx_runtime16 = require("react/jsx-runtime");
371
425
  function ContentContainer({ children }) {
372
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "container-fluid flex-grow-1", children });
426
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "container-fluid flex-grow-1", children });
373
427
  }
374
428
 
375
429
  // src/components/wrappers/ContentRow.tsx
376
- var import_jsx_runtime16 = require("react/jsx-runtime");
430
+ var import_jsx_runtime17 = require("react/jsx-runtime");
377
431
  function ContentRow({ children }) {
378
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "row h-100 flex-nowrap", children });
432
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "row h-100 flex-nowrap", children });
379
433
  }
380
434
 
381
435
  // src/components/wrappers/LayoutWrapper.tsx
382
- var import_jsx_runtime17 = require("react/jsx-runtime");
436
+ var import_jsx_runtime18 = require("react/jsx-runtime");
383
437
  function LayoutWrapper({ children }) {
384
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "d-flex flex-column min-vh-100", children });
438
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "d-flex flex-column min-vh-100", children });
385
439
  }
386
440
 
387
441
  // src/modal/Modal.tsx
388
- var import_jsx_runtime18 = require("react/jsx-runtime");
442
+ var import_jsx_runtime19 = require("react/jsx-runtime");
389
443
  var Modal = ({
390
444
  title,
391
445
  isOpen,
@@ -407,8 +461,9 @@ var Modal = ({
407
461
  const handleContentClick = (e) => {
408
462
  e.stopPropagation();
409
463
  };
410
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
411
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
464
+ const hasFooterButtons = (showCloseButton || onConfirm && showConfirmButton) && !disableAllButtons;
465
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
466
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
412
467
  "div",
413
468
  {
414
469
  className: "modal-backdrop fade show",
@@ -416,7 +471,7 @@ var Modal = ({
416
471
  onClick: handleBackdropClick
417
472
  }
418
473
  ),
419
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
474
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
420
475
  "div",
421
476
  {
422
477
  className: "modal fade show d-block",
@@ -424,44 +479,52 @@ var Modal = ({
424
479
  role: "dialog",
425
480
  style: { zIndex: 1050 },
426
481
  onClick: handleBackdropClick,
427
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "modal-dialog modal-dialog-centered", role: "document", onClick: handleContentClick, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "modal-content shadow", children: [
428
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "modal-header", children: [
429
- title && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h5", { className: "modal-title", children: title }),
430
- showHeaderClose && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
431
- "button",
432
- {
433
- type: "button",
434
- className: "btn-close",
435
- "aria-label": "Close",
436
- onClick: onClose,
437
- disabled: disableAllButtons
438
- }
439
- )
440
- ] }),
441
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "modal-body", children }),
442
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "modal-footer", children: [
443
- showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
444
- "button",
445
- {
446
- type: "button",
447
- className: "btn btn-secondary",
448
- onClick: onClose,
449
- disabled: disableAllButtons,
450
- children: "Close"
451
- }
452
- ),
453
- onConfirm && showConfirmButton && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
454
- "button",
455
- {
456
- type: "button",
457
- className: "btn btn-primary",
458
- onClick: onConfirm,
459
- disabled: disableAllButtons,
460
- children: confirmText
461
- }
462
- )
463
- ] })
464
- ] }) })
482
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
483
+ "div",
484
+ {
485
+ className: "modal-dialog modal-dialog-centered",
486
+ role: "document",
487
+ onClick: handleContentClick,
488
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "modal-content shadow", children: [
489
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "modal-header", children: [
490
+ title && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h5", { className: "modal-title", children: title }),
491
+ showHeaderClose && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
492
+ "button",
493
+ {
494
+ type: "button",
495
+ className: "btn-close",
496
+ "aria-label": "Close",
497
+ onClick: onClose,
498
+ disabled: disableAllButtons
499
+ }
500
+ )
501
+ ] }),
502
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "modal-body", children }),
503
+ hasFooterButtons && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "modal-footer", children: [
504
+ showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
505
+ "button",
506
+ {
507
+ type: "button",
508
+ className: "btn btn-secondary",
509
+ onClick: onClose,
510
+ disabled: disableAllButtons,
511
+ children: "Close"
512
+ }
513
+ ),
514
+ onConfirm && showConfirmButton && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
515
+ "button",
516
+ {
517
+ type: "button",
518
+ className: "btn btn-primary",
519
+ onClick: onConfirm,
520
+ disabled: disableAllButtons,
521
+ children: confirmText
522
+ }
523
+ )
524
+ ] })
525
+ ] })
526
+ }
527
+ )
465
528
  }
466
529
  )
467
530
  ] });
@@ -469,7 +532,7 @@ var Modal = ({
469
532
 
470
533
  // src/modal/ModalProvider.tsx
471
534
  var import_react4 = require("react");
472
- var import_jsx_runtime19 = require("react/jsx-runtime");
535
+ var import_jsx_runtime20 = require("react/jsx-runtime");
473
536
  var ModalContext = (0, import_react4.createContext)(void 0);
474
537
  function ModalProvider({ children }) {
475
538
  const [isOpen, setIsOpen] = (0, import_react4.useState)(false);
@@ -483,9 +546,9 @@ function ModalProvider({ children }) {
483
546
  const closeModal = (0, import_react4.useCallback)(() => {
484
547
  setIsOpen(false);
485
548
  }, []);
486
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(ModalContext.Provider, { value: { openModal, closeModal }, children: [
549
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(ModalContext.Provider, { value: { openModal, closeModal }, children: [
487
550
  children,
488
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
551
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
489
552
  Modal,
490
553
  {
491
554
  isOpen,
@@ -524,6 +587,7 @@ function UseModal() {
524
587
  LayoutWrapper,
525
588
  Modal,
526
589
  ModalProvider,
590
+ NavToggleButton,
527
591
  Navigation,
528
592
  NavigationItem,
529
593
  Notifications,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/components/Alert.tsx","../src/components/AppTile.tsx","../src/components/Body.tsx","../src/components/Button.tsx","../src/components/Footer.tsx","../src/components/Header.tsx","../src/components/HeaderText.tsx","../src/components/Icon.tsx","../src/components/NpmBadge.tsx","../src/components/navigation/Navigation.tsx","../src/components/navigation/NavigationItem.tsx","../src/components/notifications/Notifications.tsx","../src/components/notifications/NotificationsProvider.tsx","../src/components/views/AppsView.tsx","../src/components/wrappers/ContentContainer.tsx","../src/components/wrappers/ContentRow.tsx","../src/components/wrappers/LayoutWrapper.tsx","../src/modal/Modal.tsx","../src/modal/ModalProvider.tsx"],"sourcesContent":["/**\n * @file Automatically generated by barrelsby.\n */\n\nexport * from \"./components/Alert\";\nexport * from \"./components/AppTile\";\nexport * from \"./components/Body\";\nexport * from \"./components/Button\";\nexport * from \"./components/Footer\";\nexport * from \"./components/Header\";\nexport * from \"./components/HeaderText\";\nexport * from \"./components/Icon\";\nexport * from \"./components/NpmBadge\";\nexport * from \"./components/navigation/Navigation\";\nexport * from \"./components/navigation/NavigationItem\";\nexport * from \"./components/notifications/Notifications\";\nexport * from \"./components/notifications/NotificationsProvider\";\nexport * from \"./components/views/AppsView\";\nexport * from \"./components/wrappers/ContentContainer\";\nexport * from \"./components/wrappers/ContentRow\";\nexport * from \"./components/wrappers/LayoutWrapper\";\nexport * from \"./modal/Modal\";\nexport * from \"./modal/ModalProvider\";\nexport * from \"./models/AppItem\";\n","// components/Alert.tsx\r\nimport React from \"react\";\r\n\r\nexport type AlertVariant =\r\n | \"primary\"\r\n | \"secondary\"\r\n | \"success\"\r\n | \"danger\"\r\n | \"warning\"\r\n | \"info\"\r\n | \"light\"\r\n | \"dark\";\r\n\r\ninterface AlertProps {\r\n variant?: AlertVariant;\r\n title?: string;\r\n dismissible?: boolean;\r\n className?: string;\r\n children: React.ReactNode;\r\n onClose?: () => void;\r\n}\r\n\r\nexport const Alert: React.FC<AlertProps> = ({\r\n variant = \"info\",\r\n title,\r\n dismissible = false,\r\n className = \"\",\r\n children,\r\n onClose,\r\n}) => {\r\n const classes = [\r\n \"alert\",\r\n `alert-${variant}`,\r\n dismissible ? \"alert-dismissible fade show\" : \"\",\r\n className,\r\n ]\r\n .filter(Boolean)\r\n .join(\" \");\r\n\r\n return (\r\n <div role=\"alert\" className={classes}>\r\n {title && <strong className=\"d-block mb-1\">{title}</strong>}\r\n {children}\r\n\r\n {dismissible && (\r\n <button\r\n type=\"button\"\r\n className=\"btn-close\"\r\n aria-label=\"Close\"\r\n onClick={onClose}\r\n />\r\n )}\r\n </div>\r\n );\r\n};\r\n","import React from \"react\";\r\nimport type { AppItem } from \"../models/AppItem\";\r\n\r\ninterface AppTileProps {\r\n app: AppItem;\r\n onClick?: (app: AppItem) => void;\r\n}\r\n\r\nexport const AppTile: React.FC<AppTileProps> = ({ app, onClick }) => {\r\n return (\r\n <div className=\"col-auto\">\r\n <button\r\n type=\"button\"\r\n className=\"btn p-2 d-flex flex-column align-items-center border-0 bg-transparent app-icon-btn\"\r\n onClick={() => onClick?.(app)}\r\n >\r\n <div className=\"app-icon-tile d-flex align-items-center justify-content-center mb-1\">\r\n <span style={{fontSize:\"32px\"}}>{app.icon}</span>\r\n </div>\r\n\r\n <span className=\"w-100 small text-truncate\">\r\n {app.name}\r\n {app.version && <small className=\"text-muted ms-1\">v{app.version}</small>}\r\n </span>\r\n </button>\r\n </div>\r\n );\r\n};\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface BodyProps {\r\n icon: ReactNode;\r\n title: ReactNode;\r\n description?: ReactNode;\r\n children?: ReactNode;\r\n}\r\n\r\nexport function Body({ icon, title, description, children }: BodyProps) {\r\n return (\r\n\r\n <main className=\"col p-4\">\r\n <h2>{icon} {title}</h2>\r\n\r\n {description && <p>{description}</p>}\r\n {children}\r\n </main>\r\n );\r\n}\r\n","import { useState, type ReactNode, type MouseEvent } from \"react\";\r\n\r\nexport interface ButtonProps {\r\n buttonClassName?: string;\r\n type?: \"button\" | \"submit\" | \"reset\";\r\n disabled?: boolean;\r\n\r\n onAction?: () => Promise<unknown>;\r\n onClick?: (event: MouseEvent<HTMLButtonElement>) => void;\r\n onDone?: (result: unknown) => void;\r\n onError?: (error: unknown) => void;\r\n onFinally?: () => void;\r\n\r\n children?: ReactNode;\r\n loadingChildren?: ReactNode;\r\n\r\n renderContent?: () => ReactNode;\r\n renderLoading?: () => ReactNode;\r\n\r\n loadingText?: string;\r\n showSpinner?: boolean;\r\n}\r\n\r\nexport function Button({\r\n buttonClassName = \"btn btn-primary btn-lg\",\r\n type = \"button\",\r\n disabled = false,\r\n\r\n onAction,\r\n onClick,\r\n onDone,\r\n onError,\r\n onFinally,\r\n\r\n children = \"Click me\",\r\n\r\n loadingChildren,\r\n renderContent,\r\n renderLoading,\r\n\r\n loadingText = \"Loading...\",\r\n showSpinner = true,\r\n}: ButtonProps) {\r\n const [loading, setLoading] = useState(false);\r\n\r\n const handleClick = async (event: MouseEvent<HTMLButtonElement>) => {\r\n if (loading) return;\r\n\r\n onClick?.(event);\r\n if (event.defaultPrevented) return;\r\n if (!onAction) return;\r\n\r\n try {\r\n setLoading(true);\r\n const result = await onAction();\r\n onDone?.(result);\r\n } catch (error) {\r\n onError?.(error);\r\n } finally {\r\n setLoading(false);\r\n onFinally?.();\r\n }\r\n };\r\n\r\n return (\r\n <button\r\n type={type}\r\n className={`${buttonClassName} ${loading ? \"async-btn--loading\" : \"\"}`}\r\n onClick={handleClick}\r\n disabled={disabled || loading}\r\n >\r\n {loading ? (\r\n renderLoading?.() ??\r\n loadingChildren ?? (\r\n <>\r\n {showSpinner && (\r\n <span\r\n className=\"spinner-border spinner-border-sm me-2 async-btn__spinner\"\r\n role=\"status\"\r\n aria-hidden=\"true\"\r\n />\r\n )}\r\n {loadingText}\r\n </>\r\n )\r\n ) : (\r\n renderContent?.() ?? children\r\n )}\r\n </button>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface FooterProps {\r\n children?: ReactNode;\r\n}\r\n\r\nexport function Footer({ children }: FooterProps) {\r\n return (\r\n <footer className=\"bg-dark text-white text-center py-2 mt-auto\">\r\n {children ?? \"© 2025 Your Site — All rights reserved\"}\r\n </footer>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface HeaderProps {\r\n children?: ReactNode;\r\n}\r\n\r\nexport function Header({ children }: HeaderProps) {\r\n return (\r\n <nav className=\"navbar navbar-dark bg-dark px-3 sticky-top\">\r\n {children ?? (\r\n <span className=\"navbar-brand mb-0 h4\">My Website</span>\r\n )}\r\n </nav>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface HeaderBrandProps {\r\n children: ReactNode;\r\n}\r\n\r\nexport function HeaderText({ children }: HeaderBrandProps) {\r\n return (\r\n <span className=\"navbar-brand mb-0 h4\">\r\n {children}\r\n </span>\r\n );\r\n}\r\n","// ImageCard.tsx\r\nimport React from \"react\";\r\n\r\ninterface ImageCardProps {\r\n src: string;\r\n alt: string;\r\n width?: string | number;\r\n height?: string | number;\r\n}\r\n\r\nexport const ImageCard: React.FC<ImageCardProps> = ({\r\n src,\r\n alt,\r\n}) => {\r\n return (\r\n <img\r\n src={src}\r\n alt={alt}\r\n className={\"w-100\"}\r\n style={{\r\n display: \"block\",\r\n objectFit: \"cover\",\r\n }}\r\n />\r\n );\r\n};\r\n","// components/NpmBadge.tsx\r\ninterface NpmBadgeProps {\r\n packageLabel: string;\r\n packageName: string;\r\n packageUrl: string; // e.g. \"@sparkstudio/authentication-ui\"\r\n color?: string; // optional (default black)\r\n height?: number; // optional (default 22)\r\n}\r\n\r\nexport function NpmBadge({ packageName, packageLabel, packageUrl, color = \"black\", height = 22 }: NpmBadgeProps) {\r\n const encodedLabel = encodeURIComponent(packageLabel);\r\n const badgeUrl = `https://img.shields.io/npm/v/${packageName}?logo=npm&label=${encodedLabel}&color=${color}`;\r\n const npmUrl = packageUrl;\r\n\r\n return (\r\n <a href={npmUrl} target=\"_blank\" rel=\"noopener noreferrer\">\r\n <img src={badgeUrl} alt={`${packageName} version`} style={{ height }} />\r\n </a>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface NavigationProps {\r\n title?: ReactNode;\r\n children: ReactNode;\r\n}\r\n\r\nexport function Navigation({ title = \"Navigation\", children }: NavigationProps) {\r\n return (\r\n <nav className=\"col-auto p-3 h-100 d-flex flex-column\">\r\n <h6 className=\"text-muted text-uppercase\">{title}</h6>\r\n\r\n <ul className=\"nav nav-pills flex-column gap-2 mt-3\">\r\n {children}\r\n </ul>\r\n </nav>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface NavigationItemProps {\r\n icon: ReactNode;\r\n title: ReactNode;\r\n type: string;\r\n active?: boolean;\r\n onClick?: (type: string) => void;\r\n}\r\n\r\nexport function NavigationItem({icon, title, type, active, onClick }: NavigationItemProps) {\r\n return (\r\n <li className=\"nav-item\">\r\n <button\r\n type=\"button\"\r\n className={`nav-link text-start w-100 ${active ? \"active\" : \"\"}`}\r\n onClick={() => onClick?.(type)}\r\n >\r\n {icon} {title}\r\n </button>\r\n </li>\r\n );\r\n}\r\n","import { useEffect } from \"react\";\r\n\r\nexport type NotificationType = \"success\" | \"info\" | \"warning\" | \"danger\";\r\n\r\nexport interface Notification {\r\n id: string;\r\n message: string;\r\n type?: NotificationType;\r\n autoCloseMs?: number;\r\n}\r\n\r\ninterface NotificationsProps {\r\n notifications: Notification[];\r\n onClose: (id: string) => void;\r\n}\r\n\r\nexport function Notifications({ notifications, onClose }: NotificationsProps) {\r\n useEffect(() => {\r\n notifications.forEach((n) => {\r\n if (!n.autoCloseMs) return;\r\n\r\n const timer = setTimeout(() => onClose(n.id), n.autoCloseMs);\r\n return () => clearTimeout(timer);\r\n });\r\n }, [notifications, onClose]);\r\n\r\n return (\r\n <div\r\n className=\"position-fixed top-0 end-0\"\r\n style={{ zIndex: 1080, width: 340 }}\r\n >\r\n {notifications.map((n) => (\r\n <div\r\n key={n.id}\r\n className={`alert alert-${n.type ?? \"info\"} alert-dismissible fade show shadow-sm mb-2`}\r\n role=\"alert\"\r\n >\r\n {n.message}\r\n\r\n <button\r\n type=\"button\"\r\n className=\"btn-close\"\r\n aria-label=\"Close\"\r\n onClick={() => onClose(n.id)}\r\n />\r\n </div>\r\n ))}\r\n </div>\r\n );\r\n}\r\n","import {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useEffect,\r\n useState,\r\n type ReactNode,\r\n} from \"react\";\r\nimport type { NotificationType } from \"./Notifications\";\r\n\r\n// Rename to avoid conflict with browser Notification type\r\nexport interface AppNotification {\r\n id: string;\r\n message: string;\r\n type?: NotificationType;\r\n autoCloseMs?: number;\r\n}\r\n\r\ninterface NotificationsContextValue {\r\n push: (message: string, type?: NotificationType, autoCloseMs?: number) => void;\r\n}\r\n\r\nconst NotificationsContext = createContext<NotificationsContextValue | undefined>(\r\n undefined\r\n);\r\n\r\ninterface NotificationsProviderProps {\r\n children: ReactNode;\r\n}\r\n\r\nexport function NotificationsProvider({ children }: NotificationsProviderProps) {\r\n const [notifications, setNotifications] = useState<AppNotification[]>([]);\r\n\r\n const push = useCallback(\r\n (message: string, type: NotificationType = \"info\", autoCloseMs = 4000) => {\r\n setNotifications((prev) => [\r\n ...prev,\r\n {\r\n id: crypto.randomUUID(),\r\n message,\r\n type,\r\n autoCloseMs,\r\n },\r\n ]);\r\n },\r\n []\r\n );\r\n\r\n const close = useCallback((id: string) => {\r\n setNotifications((list) => list.filter((n) => n.id !== id));\r\n }, []);\r\n\r\n // Handle auto close timers\r\n useEffect(() => {\r\n const timers = notifications.map((n) => {\r\n if (!n.autoCloseMs) return null;\r\n return setTimeout(() => close(n.id), n.autoCloseMs);\r\n });\r\n\r\n return () => {\r\n timers.forEach((t) => t && clearTimeout(t));\r\n };\r\n }, [notifications, close]);\r\n\r\n return (\r\n <NotificationsContext.Provider value={{ push }}>\r\n {/* overlay renderer */}\r\n <div\r\n className=\"position-fixed top-0 end-0 p-3\"\r\n style={{ zIndex: 1080, width: 340 }}\r\n >\r\n {notifications.map((n) => (\r\n <div\r\n key={n.id}\r\n className={`alert alert-${n.type ?? \"info\"} alert-dismissible fade show shadow-sm mb-2`}\r\n role=\"alert\"\r\n >\r\n {n.message}\r\n <button\r\n type=\"button\"\r\n className=\"btn-close\"\r\n aria-label=\"Close\"\r\n onClick={() => close(n.id)}\r\n />\r\n </div>\r\n ))}\r\n </div>\r\n\r\n {children}\r\n </NotificationsContext.Provider>\r\n );\r\n}\r\n\r\nexport function UseNotifications(): NotificationsContextValue {\r\n const ctx = useContext(NotificationsContext);\r\n if (!ctx) {\r\n throw new Error(\"useNotifications must be used within a NotificationsProvider\");\r\n }\r\n return ctx;\r\n}\r\n","// ./views/AppsView.tsx (or ./components/AppsView.tsx)\r\nimport React from \"react\";\r\nimport type { AppItem } from \"../../models/AppItem\";\r\nimport { AppTile } from \"../AppTile\";\r\n\r\ninterface AppsViewProps {\r\n apps: AppItem[];\r\n title?: string;\r\n onAppClick?: (app: AppItem) => void;\r\n}\r\n\r\nexport const AppsView: React.FC<AppsViewProps> = ({ apps, title, onAppClick }) => {\r\n return (\r\n <>\r\n {title && <h5 className=\"mb-3\">{title}</h5>}\r\n\r\n <div className=\"row\">\r\n {apps.map((app) => (\r\n <AppTile key={app.id} app={app} onClick={onAppClick} />\r\n ))}\r\n </div>\r\n </>\r\n );\r\n};\r\n","export function ContentContainer({ children }: React.PropsWithChildren) {\r\n return <div className=\"container-fluid flex-grow-1\">{children}</div>;\r\n}\r\n","export function ContentRow({ children }: React.PropsWithChildren) {\r\n return <div className=\"row h-100 flex-nowrap\">{children}</div>;\r\n}\r\n","\r\nexport function LayoutWrapper({ children }: React.PropsWithChildren) {\r\n return (\r\n\r\n <div className=\"d-flex flex-column min-vh-100\">{children}</div>\r\n );\r\n}\r\n","// components/Modal.tsx\r\nimport React from \"react\";\r\n\r\nexport interface ModalProps {\r\n title?: string;\r\n isOpen: boolean;\r\n onClose: () => void;\r\n onConfirm?: () => void;\r\n confirmText?: string;\r\n\r\n disableBackdropClose?: boolean;\r\n disableAllButtons?: boolean;\r\n\r\n /** NEW: toggle visibility */\r\n showHeaderClose?: boolean;\r\n showCloseButton?: boolean;\r\n showConfirmButton?: boolean;\r\n\r\n children?: React.ReactNode;\r\n}\r\n\r\nexport const Modal: React.FC<ModalProps> = ({\r\n title,\r\n isOpen,\r\n onClose,\r\n onConfirm,\r\n confirmText = \"OK\",\r\n\r\n disableBackdropClose = false,\r\n disableAllButtons = false,\r\n\r\n showHeaderClose = true,\r\n showCloseButton = true,\r\n showConfirmButton = true,\r\n\r\n children,\r\n}) => {\r\n if (!isOpen) return null;\r\n\r\n const handleBackdropClick = () => {\r\n if (disableBackdropClose || disableAllButtons) return;\r\n onClose();\r\n };\r\n\r\n const handleContentClick: React.MouseEventHandler<HTMLDivElement> = (e) => {\r\n e.stopPropagation();\r\n };\r\n\r\n return (\r\n <>\r\n <div\r\n className=\"modal-backdrop fade show\"\r\n style={{ zIndex: 1040 }}\r\n onClick={handleBackdropClick}\r\n />\r\n\r\n <div\r\n className=\"modal fade show d-block\"\r\n tabIndex={-1}\r\n role=\"dialog\"\r\n style={{ zIndex: 1050 }}\r\n onClick={handleBackdropClick}\r\n >\r\n <div className=\"modal-dialog modal-dialog-centered\" role=\"document\" onClick={handleContentClick}>\r\n <div className=\"modal-content shadow\">\r\n {/* Header */}\r\n <div className=\"modal-header\">\r\n {title && <h5 className=\"modal-title\">{title}</h5>}\r\n\r\n {showHeaderClose && (\r\n <button\r\n type=\"button\"\r\n className=\"btn-close\"\r\n aria-label=\"Close\"\r\n onClick={onClose}\r\n disabled={disableAllButtons}\r\n />\r\n )}\r\n </div>\r\n\r\n {/* Body */}\r\n <div className=\"modal-body\">{children}</div>\r\n\r\n {/* Footer */}\r\n <div className=\"modal-footer\">\r\n {showCloseButton && (\r\n <button\r\n type=\"button\"\r\n className=\"btn btn-secondary\"\r\n onClick={onClose}\r\n disabled={disableAllButtons}\r\n >\r\n Close\r\n </button>\r\n )}\r\n\r\n {onConfirm && showConfirmButton && (\r\n <button\r\n type=\"button\"\r\n className=\"btn btn-primary\"\r\n onClick={onConfirm}\r\n disabled={disableAllButtons}\r\n >\r\n {confirmText}\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n};\r\n","import {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useState,\r\n type ReactNode,\r\n} from \"react\";\r\nimport { Modal } from \"./Modal\";\r\n\r\nexport interface ModalOptions {\r\n title?: string;\r\n confirmText?: string;\r\n disableBackdropClose?: boolean;\r\n onConfirm?: () => void;\r\n\r\n /** extra control flags matching Modal.tsx */\r\n disableAllButtons?: boolean;\r\n showHeaderClose?: boolean;\r\n showCloseButton?: boolean;\r\n showConfirmButton?: boolean;\r\n}\r\n\r\ninterface ModalContextValue {\r\n openModal: (content: ReactNode, options?: ModalOptions) => void;\r\n closeModal: () => void;\r\n}\r\n\r\nconst ModalContext = createContext<ModalContextValue | undefined>(undefined);\r\n\r\nexport function ModalProvider({ children }: { children: ReactNode }) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [content, setContent] = useState<ReactNode>(null);\r\n const [options, setOptions] = useState<ModalOptions>({});\r\n\r\n const openModal = useCallback((content: ReactNode, options?: ModalOptions) => {\r\n setContent(content);\r\n setOptions(options ?? {});\r\n setIsOpen(true);\r\n }, []);\r\n\r\n const closeModal = useCallback(() => {\r\n setIsOpen(false);\r\n }, []);\r\n\r\n return (\r\n <ModalContext.Provider value={{ openModal, closeModal }}>\r\n {children}\r\n\r\n <Modal\r\n isOpen={isOpen}\r\n title={options.title}\r\n confirmText={options.confirmText}\r\n disableBackdropClose={options.disableBackdropClose}\r\n disableAllButtons={options.disableAllButtons}\r\n showHeaderClose={options.showHeaderClose}\r\n showCloseButton={options.showCloseButton}\r\n showConfirmButton={options.showConfirmButton}\r\n onClose={closeModal}\r\n onConfirm={options.onConfirm}\r\n >\r\n {content}\r\n </Modal>\r\n </ModalContext.Provider>\r\n );\r\n}\r\n\r\nexport function UseModal() {\r\n const ctx = useContext(ModalContext);\r\n if (!ctx) throw new Error(\"UseModal must be used within ModalProvider\");\r\n return ctx;\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACwCI;AAlBG,IAAM,QAA8B,CAAC;AAAA,EAC1C,UAAU;AAAA,EACV;AAAA,EACA,cAAc;AAAA,EACd,YAAY;AAAA,EACZ;AAAA,EACA;AACF,MAAM;AACJ,QAAM,UAAU;AAAA,IACd;AAAA,IACA,SAAS,OAAO;AAAA,IAChB,cAAc,gCAAgC;AAAA,IAC9C;AAAA,EACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,SACE,6CAAC,SAAI,MAAK,SAAQ,WAAW,SAC1B;AAAA,aAAS,4CAAC,YAAO,WAAU,gBAAgB,iBAAM;AAAA,IACjD;AAAA,IAEA,eACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,QACV,cAAW;AAAA,QACX,SAAS;AAAA;AAAA,IACX;AAAA,KAEJ;AAEJ;;;ACrCU,IAAAA,sBAAA;AATH,IAAM,UAAkC,CAAC,EAAE,KAAK,QAAQ,MAAM;AACnE,SACE,6CAAC,SAAI,WAAU,YACb;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAU;AAAA,MACV,SAAS,MAAM,UAAU,GAAG;AAAA,MAE5B;AAAA,qDAAC,SAAI,WAAU,uEACb,uDAAC,UAAK,OAAO,EAAC,UAAS,OAAM,GAAI,cAAI,MAAK,GAC5C;AAAA,QAEA,8CAAC,UAAK,WAAU,6BACb;AAAA,cAAI;AAAA,UACJ,IAAI,WAAW,8CAAC,WAAM,WAAU,mBAAkB;AAAA;AAAA,YAAE,IAAI;AAAA,aAAQ;AAAA,WACnE;AAAA;AAAA;AAAA,EACF,GACF;AAEJ;;;ACdQ,IAAAC,sBAAA;AAJD,SAAS,KAAK,EAAE,MAAM,OAAO,aAAa,SAAS,GAAc;AACtE,SAEI,8CAAC,UAAK,WAAU,WACd;AAAA,kDAAC,QAAI;AAAA;AAAA,MAAK;AAAA,MAAE;AAAA,OAAM;AAAA,IAEjB,eAAe,6CAAC,OAAG,uBAAY;AAAA,IAC/B;AAAA,KACH;AAEN;;;ACnBA,mBAA0D;AA0EhD,IAAAC,sBAAA;AAnDH,SAAS,OAAO;AAAA,EACrB,kBAAkB;AAAA,EAClB,OAAO;AAAA,EACP,WAAW;AAAA,EAEX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,WAAW;AAAA,EAEX;AAAA,EACA;AAAA,EACA;AAAA,EAEA,cAAc;AAAA,EACd,cAAc;AAChB,GAAgB;AACd,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,KAAK;AAE5C,QAAM,cAAc,OAAO,UAAyC;AAClE,QAAI,QAAS;AAEb,cAAU,KAAK;AACf,QAAI,MAAM,iBAAkB;AAC5B,QAAI,CAAC,SAAU;AAEf,QAAI;AACF,iBAAW,IAAI;AACf,YAAM,SAAS,MAAM,SAAS;AAC9B,eAAS,MAAM;AAAA,IACjB,SAAS,OAAO;AACd,gBAAU,KAAK;AAAA,IACjB,UAAE;AACA,iBAAW,KAAK;AAChB,kBAAY;AAAA,IACd;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAW,GAAG,eAAe,IAAI,UAAU,uBAAuB,EAAE;AAAA,MACpE,SAAS;AAAA,MACT,UAAU,YAAY;AAAA,MAErB,oBACC,gBAAgB,KAChB,mBACE,8EACG;AAAA,uBACC;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,MAAK;AAAA,YACL,eAAY;AAAA;AAAA,QACd;AAAA,QAED;AAAA,SACH,IAGF,gBAAgB,KAAK;AAAA;AAAA,EAEzB;AAEJ;;;AClFI,IAAAC,sBAAA;AAFG,SAAS,OAAO,EAAE,SAAS,GAAgB;AAChD,SACE,6CAAC,YAAO,WAAU,+CACf,sBAAY,kDACf;AAEJ;;;ACFQ,IAAAC,sBAAA;AAJD,SAAS,OAAO,EAAE,SAAS,GAAgB;AAChD,SACE,6CAAC,SAAI,WAAU,8CACZ,sBACC,6CAAC,UAAK,WAAU,wBAAuB,wBAAU,GAErD;AAEJ;;;ACNI,IAAAC,sBAAA;AAFG,SAAS,WAAW,EAAE,SAAS,GAAqB;AACzD,SACE,6CAAC,UAAK,WAAU,wBACb,UACH;AAEJ;;;ACGI,IAAAC,sBAAA;AALG,IAAM,YAAsC,CAAC;AAAA,EAClD;AAAA,EACA;AACF,MAAM;AACJ,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX,OAAO;AAAA,QACL,SAAS;AAAA,QACT,WAAW;AAAA,MACb;AAAA;AAAA,EACF;AAEJ;;;ACTM,IAAAC,sBAAA;AAPC,SAAS,SAAS,EAAE,aAAa,cAAc,YAAY,QAAQ,SAAS,SAAS,GAAG,GAAkB;AAC/G,QAAM,eAAe,mBAAmB,YAAY;AACpD,QAAM,WAAW,gCAAgC,WAAW,mBAAmB,YAAY,UAAU,KAAK;AAC1G,QAAM,SAAS;AAEf,SACE,6CAAC,OAAE,MAAM,QAAQ,QAAO,UAAS,KAAI,uBACnC,uDAAC,SAAI,KAAK,UAAU,KAAK,GAAG,WAAW,YAAY,OAAO,EAAE,OAAO,GAAG,GACxE;AAEJ;;;ACVI,IAAAC,uBAAA;AAFG,SAAS,WAAW,EAAE,QAAQ,cAAc,SAAS,GAAoB;AAC9E,SACE,+CAAC,SAAI,WAAU,yCACb;AAAA,kDAAC,QAAG,WAAU,6BAA6B,iBAAM;AAAA,IAEjD,8CAAC,QAAG,WAAU,wCACX,UACH;AAAA,KACF;AAEJ;;;ACLI,IAAAC,uBAAA;AAFG,SAAS,eAAe,EAAC,MAAM,OAAO,MAAM,QAAQ,QAAQ,GAAwB;AACzF,SACE,8CAAC,QAAG,WAAU,YACZ;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAW,6BAA6B,SAAS,WAAW,EAAE;AAAA,MAC9D,SAAS,MAAM,UAAU,IAAI;AAAA,MAE5B;AAAA;AAAA,QAAK;AAAA,QAAE;AAAA;AAAA;AAAA,EACV,GACF;AAEJ;;;ACtBA,IAAAC,gBAA0B;AAgClB,IAAAC,uBAAA;AAhBD,SAAS,cAAc,EAAE,eAAe,QAAQ,GAAuB;AAC5E,+BAAU,MAAM;AACd,kBAAc,QAAQ,CAAC,MAAM;AAC3B,UAAI,CAAC,EAAE,YAAa;AAEpB,YAAM,QAAQ,WAAW,MAAM,QAAQ,EAAE,EAAE,GAAG,EAAE,WAAW;AAC3D,aAAO,MAAM,aAAa,KAAK;AAAA,IACjC,CAAC;AAAA,EACH,GAAG,CAAC,eAAe,OAAO,CAAC;AAE3B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO,EAAE,QAAQ,MAAM,OAAO,IAAI;AAAA,MAEjC,wBAAc,IAAI,CAAC,MAClB;AAAA,QAAC;AAAA;AAAA,UAEC,WAAW,eAAe,EAAE,QAAQ,MAAM;AAAA,UAC1C,MAAK;AAAA,UAEJ;AAAA,cAAE;AAAA,YAEH;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,cAAW;AAAA,gBACX,SAAS,MAAM,QAAQ,EAAE,EAAE;AAAA;AAAA,YAC7B;AAAA;AAAA;AAAA,QAXK,EAAE;AAAA,MAYT,CACD;AAAA;AAAA,EACH;AAEJ;;;ACjDA,IAAAC,gBAOO;AAiEG,IAAAC,uBAAA;AAlDV,IAAM,2BAAuB;AAAA,EAC3B;AACF;AAMO,SAAS,sBAAsB,EAAE,SAAS,GAA+B;AAC9E,QAAM,CAAC,eAAe,gBAAgB,QAAI,wBAA4B,CAAC,CAAC;AAExE,QAAM,WAAO;AAAA,IACX,CAAC,SAAiB,OAAyB,QAAQ,cAAc,QAAS;AACxE,uBAAiB,CAAC,SAAS;AAAA,QACzB,GAAG;AAAA,QACH;AAAA,UACE,IAAI,OAAO,WAAW;AAAA,UACtB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,YAAQ,2BAAY,CAAC,OAAe;AACxC,qBAAiB,CAAC,SAAS,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;AAAA,EAC5D,GAAG,CAAC,CAAC;AAGL,+BAAU,MAAM;AACd,UAAM,SAAS,cAAc,IAAI,CAAC,MAAM;AACtC,UAAI,CAAC,EAAE,YAAa,QAAO;AAC3B,aAAO,WAAW,MAAM,MAAM,EAAE,EAAE,GAAG,EAAE,WAAW;AAAA,IACpD,CAAC;AAED,WAAO,MAAM;AACX,aAAO,QAAQ,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC;AAAA,IAC5C;AAAA,EACF,GAAG,CAAC,eAAe,KAAK,CAAC;AAEzB,SACE,+CAAC,qBAAqB,UAArB,EAA8B,OAAO,EAAE,KAAK,GAE3C;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO,EAAE,QAAQ,MAAM,OAAO,IAAI;AAAA,QAEjC,wBAAc,IAAI,CAAC,MAClB;AAAA,UAAC;AAAA;AAAA,YAEC,WAAW,eAAe,EAAE,QAAQ,MAAM;AAAA,YAC1C,MAAK;AAAA,YAEJ;AAAA,gBAAE;AAAA,cACH;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,WAAU;AAAA,kBACV,cAAW;AAAA,kBACX,SAAS,MAAM,MAAM,EAAE,EAAE;AAAA;AAAA,cAC3B;AAAA;AAAA;AAAA,UAVK,EAAE;AAAA,QAWT,CACD;AAAA;AAAA,IACH;AAAA,IAEC;AAAA,KACH;AAEJ;AAEO,SAAS,mBAA8C;AAC5D,QAAM,UAAM,0BAAW,oBAAoB;AAC3C,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,8DAA8D;AAAA,EAChF;AACA,SAAO;AACT;;;ACtFI,IAAAC,uBAAA;AAFG,IAAM,WAAoC,CAAC,EAAE,MAAM,OAAO,WAAW,MAAM;AAChF,SACE,gFACG;AAAA,aAAS,8CAAC,QAAG,WAAU,QAAQ,iBAAM;AAAA,IAEtC,8CAAC,SAAI,WAAU,OACZ,eAAK,IAAI,CAAC,QACT,8CAAC,WAAqB,KAAU,SAAS,cAA3B,IAAI,EAAmC,CACtD,GACH;AAAA,KACF;AAEJ;;;ACtBS,IAAAC,uBAAA;AADF,SAAS,iBAAiB,EAAE,SAAS,GAA4B;AACtE,SAAO,8CAAC,SAAI,WAAU,+BAA+B,UAAS;AAChE;;;ACDS,IAAAC,uBAAA;AADF,SAAS,WAAW,EAAE,SAAS,GAA4B;AAChE,SAAO,8CAAC,SAAI,WAAU,yBAAyB,UAAS;AAC1D;;;ACEM,IAAAC,uBAAA;AAHC,SAAS,cAAc,EAAE,SAAS,GAA4B;AACnE,SAEI,8CAAC,SAAI,WAAU,iCAAiC,UAAS;AAE/D;;;AC2CI,IAAAC,uBAAA;AA5BG,IAAM,QAA8B,CAAC;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EAEd,uBAAuB;AAAA,EACvB,oBAAoB;AAAA,EAEpB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EAEpB;AACF,MAAM;AACJ,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,sBAAsB,MAAM;AAChC,QAAI,wBAAwB,kBAAmB;AAC/C,YAAQ;AAAA,EACV;AAEA,QAAM,qBAA8D,CAAC,MAAM;AACzE,MAAE,gBAAgB;AAAA,EACpB;AAEA,SACE,gFACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO,EAAE,QAAQ,KAAK;AAAA,QACtB,SAAS;AAAA;AAAA,IACX;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,UAAU;AAAA,QACV,MAAK;AAAA,QACL,OAAO,EAAE,QAAQ,KAAK;AAAA,QACtB,SAAS;AAAA,QAET,wDAAC,SAAI,WAAU,sCAAqC,MAAK,YAAW,SAAS,oBAC3E,yDAAC,SAAI,WAAU,wBAEb;AAAA,yDAAC,SAAI,WAAU,gBACZ;AAAA,qBAAS,8CAAC,QAAG,WAAU,eAAe,iBAAM;AAAA,YAE5C,mBACC;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,cAAW;AAAA,gBACX,SAAS;AAAA,gBACT,UAAU;AAAA;AAAA,YACZ;AAAA,aAEJ;AAAA,UAGA,8CAAC,SAAI,WAAU,cAAc,UAAS;AAAA,UAGtC,+CAAC,SAAI,WAAU,gBACZ;AAAA,+BACC;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,SAAS;AAAA,gBACT,UAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,YAGD,aAAa,qBACZ;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,SAAS;AAAA,gBACT,UAAU;AAAA,gBAET;AAAA;AAAA,YACH;AAAA,aAEJ;AAAA,WACF,GACF;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;;;AChHA,IAAAC,gBAMO;AAuCH,IAAAC,uBAAA;AAlBJ,IAAM,mBAAe,6BAA6C,MAAS;AAEpE,SAAS,cAAc,EAAE,SAAS,GAA4B;AACnE,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAAS,KAAK;AAC1C,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAoB,IAAI;AACtD,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAuB,CAAC,CAAC;AAEvD,QAAM,gBAAY,2BAAY,CAACC,UAAoBC,aAA2B;AAC5E,eAAWD,QAAO;AAClB,eAAWC,YAAW,CAAC,CAAC;AACxB,cAAU,IAAI;AAAA,EAChB,GAAG,CAAC,CAAC;AAEL,QAAM,iBAAa,2BAAY,MAAM;AACnC,cAAU,KAAK;AAAA,EACjB,GAAG,CAAC,CAAC;AAEL,SACE,+CAAC,aAAa,UAAb,EAAsB,OAAO,EAAE,WAAW,WAAW,GACnD;AAAA;AAAA,IAED;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,OAAO,QAAQ;AAAA,QACf,aAAa,QAAQ;AAAA,QACrB,sBAAsB,QAAQ;AAAA,QAC9B,mBAAmB,QAAQ;AAAA,QAC3B,iBAAiB,QAAQ;AAAA,QACzB,iBAAiB,QAAQ;AAAA,QACzB,mBAAmB,QAAQ;AAAA,QAC3B,SAAS;AAAA,QACT,WAAW,QAAQ;AAAA,QAElB;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;AAEO,SAAS,WAAW;AACzB,QAAM,UAAM,0BAAW,YAAY;AACnC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,4CAA4C;AACtE,SAAO;AACT;","names":["import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","content","options"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/components/Alert.tsx","../src/components/AppTile.tsx","../src/components/Body.tsx","../src/components/Button.tsx","../src/components/Footer.tsx","../src/components/Header.tsx","../src/components/HeaderText.tsx","../src/components/Icon.tsx","../src/components/NpmBadge.tsx","../src/components/navigation/Navigation.tsx","../src/components/navigation/NavigationItem.tsx","../src/components/navigation/NavToggleButton.tsx","../src/components/notifications/Notifications.tsx","../src/components/notifications/NotificationsProvider.tsx","../src/components/views/AppsView.tsx","../src/components/wrappers/ContentContainer.tsx","../src/components/wrappers/ContentRow.tsx","../src/components/wrappers/LayoutWrapper.tsx","../src/modal/Modal.tsx","../src/modal/ModalProvider.tsx"],"sourcesContent":["/**\n * @file Automatically generated by barrelsby.\n */\n\nexport * from \"./components/Alert\";\nexport * from \"./components/AppTile\";\nexport * from \"./components/Body\";\nexport * from \"./components/Button\";\nexport * from \"./components/Footer\";\nexport * from \"./components/Header\";\nexport * from \"./components/HeaderText\";\nexport * from \"./components/Icon\";\nexport * from \"./components/NpmBadge\";\nexport * from \"./components/navigation/Navigation\";\nexport * from \"./components/navigation/NavigationItem\";\nexport * from \"./components/navigation/NavToggleButton\";\nexport * from \"./components/notifications/Notifications\";\nexport * from \"./components/notifications/NotificationsProvider\";\nexport * from \"./components/views/AppsView\";\nexport * from \"./components/wrappers/ContentContainer\";\nexport * from \"./components/wrappers/ContentRow\";\nexport * from \"./components/wrappers/LayoutWrapper\";\nexport * from \"./modal/Modal\";\nexport * from \"./modal/ModalProvider\";\nexport * from \"./models/AppItem\";\n","// components/Alert.tsx\r\nimport React from \"react\";\r\n\r\nexport type AlertVariant =\r\n | \"primary\"\r\n | \"secondary\"\r\n | \"success\"\r\n | \"danger\"\r\n | \"warning\"\r\n | \"info\"\r\n | \"light\"\r\n | \"dark\";\r\n\r\ninterface AlertProps {\r\n variant?: AlertVariant;\r\n title?: string;\r\n dismissible?: boolean;\r\n className?: string;\r\n children: React.ReactNode;\r\n onClose?: () => void;\r\n}\r\n\r\nexport const Alert: React.FC<AlertProps> = ({\r\n variant = \"info\",\r\n title,\r\n dismissible = false,\r\n className = \"\",\r\n children,\r\n onClose,\r\n}) => {\r\n const classes = [\r\n \"alert\",\r\n `alert-${variant}`,\r\n dismissible ? \"alert-dismissible fade show\" : \"\",\r\n className,\r\n ]\r\n .filter(Boolean)\r\n .join(\" \");\r\n\r\n return (\r\n <div role=\"alert\" className={classes}>\r\n {title && <strong className=\"d-block mb-1\">{title}</strong>}\r\n {children}\r\n\r\n {dismissible && (\r\n <button\r\n type=\"button\"\r\n className=\"btn-close\"\r\n aria-label=\"Close\"\r\n onClick={onClose}\r\n />\r\n )}\r\n </div>\r\n );\r\n};\r\n","import React from \"react\";\r\nimport type { AppItem } from \"../models/AppItem\";\r\n\r\ninterface AppTileProps {\r\n app: AppItem;\r\n onClick?: (app: AppItem) => void;\r\n}\r\n\r\nexport const AppTile: React.FC<AppTileProps> = ({ app, onClick }) => {\r\n return (\r\n <div className=\"col-auto\">\r\n <button\r\n type=\"button\"\r\n className=\"btn p-2 d-flex flex-column align-items-center border-0 bg-transparent app-icon-btn\"\r\n onClick={() => onClick?.(app)}\r\n >\r\n <div className=\"app-icon-tile d-flex align-items-center justify-content-center mb-1\">\r\n <span style={{fontSize:\"32px\"}}>{app.icon}</span>\r\n </div>\r\n\r\n <span className=\"w-100 small text-truncate\">\r\n {app.name}\r\n {app.version && <small className=\"text-muted ms-1\">v{app.version}</small>}\r\n </span>\r\n </button>\r\n </div>\r\n );\r\n};\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface BodyProps {\r\n icon: ReactNode;\r\n title: ReactNode;\r\n description?: ReactNode;\r\n children?: ReactNode;\r\n}\r\n\r\nexport function Body({ icon, title, description, children }: BodyProps) {\r\n return (\r\n\r\n <main className=\"col p-4\">\r\n <h2>{icon} {title}</h2>\r\n\r\n {description && <p>{description}</p>}\r\n {children}\r\n </main>\r\n );\r\n}\r\n","import { useState, type ReactNode, type MouseEvent } from \"react\";\r\n\r\nexport type ButtonVariant =\r\n | \"primary\"\r\n | \"secondary\"\r\n | \"success\"\r\n | \"danger\"\r\n | \"warning\"\r\n | \"info\"\r\n | \"light\"\r\n | \"dark\"\r\n | \"link\";\r\n\r\nexport interface ButtonProps {\r\n buttonClassName?: string;\r\n type?: \"button\" | \"submit\" | \"reset\";\r\n disabled?: boolean;\r\n\r\n variant?: ButtonVariant; // 👈 NEW — Bootstrap color support\r\n\r\n onAction?: () => Promise<unknown>;\r\n onClick?: (event: MouseEvent<HTMLButtonElement>) => void;\r\n onDone?: (result: unknown) => void;\r\n onError?: (error: unknown) => void;\r\n onFinally?: () => void;\r\n\r\n children?: ReactNode;\r\n loadingChildren?: ReactNode;\r\n\r\n renderContent?: () => ReactNode;\r\n renderLoading?: () => ReactNode;\r\n\r\n loadingText?: string;\r\n showSpinner?: boolean;\r\n}\r\n\r\nexport function Button({\r\n buttonClassName = \"btn btn-primary btn-lg\",\r\n type = \"button\",\r\n disabled = false,\r\n\r\n variant,\r\n\r\n onAction,\r\n onClick,\r\n onDone,\r\n onError,\r\n onFinally,\r\n\r\n children = \"Click me\",\r\n\r\n loadingChildren,\r\n renderContent,\r\n renderLoading,\r\n\r\n loadingText = \"Loading...\",\r\n showSpinner = true,\r\n}: ButtonProps) {\r\n const [loading, setLoading] = useState(false);\r\n\r\n const handleClick = async (event: MouseEvent<HTMLButtonElement>) => {\r\n if (loading) return;\r\n\r\n onClick?.(event);\r\n if (event.defaultPrevented) return;\r\n if (!onAction) return;\r\n\r\n try {\r\n setLoading(true);\r\n const result = await onAction();\r\n onDone?.(result);\r\n } catch (error) {\r\n onError?.(error);\r\n } finally {\r\n setLoading(false);\r\n onFinally?.();\r\n }\r\n };\r\n\r\n // 👇 Decide which CSS to use:\r\n // - if variant is provided, use Bootstrap \"btn btn-X\"\r\n // - otherwise fall back to user-supplied buttonClassName\r\n const baseClass = variant\r\n ? `btn btn-${variant}`\r\n : buttonClassName;\r\n\r\n return (\r\n <button\r\n type={type}\r\n className={`${baseClass} ${loading ? \"async-btn--loading\" : \"\"}`}\r\n onClick={handleClick}\r\n disabled={disabled || loading}\r\n >\r\n {loading ? (\r\n renderLoading?.() ??\r\n loadingChildren ?? (\r\n <>\r\n {showSpinner && (\r\n <span\r\n className=\"spinner-border spinner-border-sm me-2 async-btn__spinner\"\r\n role=\"status\"\r\n aria-hidden=\"true\"\r\n />\r\n )}\r\n {loadingText}\r\n </>\r\n )\r\n ) : (\r\n renderContent?.() ?? children\r\n )}\r\n </button>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface FooterProps {\r\n children?: ReactNode;\r\n}\r\n\r\nexport function Footer({ children }: FooterProps) {\r\n return (\r\n <footer className=\"bg-dark text-white text-center py-2 mt-auto\">\r\n {children ?? \"© 2025 Your Site — All rights reserved\"}\r\n </footer>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface HeaderProps {\r\n children?: ReactNode;\r\n}\r\n\r\nexport function Header({ children }: HeaderProps) {\r\n return (\r\n <nav className=\"navbar navbar-dark bg-dark px-3 sticky-top\">\r\n {children ?? (\r\n <span className=\"navbar-brand mb-0 h4\">My Website</span>\r\n )}\r\n </nav>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface HeaderBrandProps {\r\n children: ReactNode;\r\n}\r\n\r\nexport function HeaderText({ children }: HeaderBrandProps) {\r\n return (\r\n <span className=\"navbar-brand mb-0 h4\">\r\n {children}\r\n </span>\r\n );\r\n}\r\n","// ImageCard.tsx\r\nimport React from \"react\";\r\n\r\ninterface ImageCardProps {\r\n src: string;\r\n alt: string;\r\n width?: string | number;\r\n height?: string | number;\r\n}\r\n\r\nexport const ImageCard: React.FC<ImageCardProps> = ({\r\n src,\r\n alt,\r\n}) => {\r\n return (\r\n <img\r\n src={src}\r\n alt={alt}\r\n className={\"w-100\"}\r\n style={{\r\n display: \"block\",\r\n objectFit: \"cover\",\r\n }}\r\n />\r\n );\r\n};\r\n","// components/NpmBadge.tsx\r\ninterface NpmBadgeProps {\r\n packageLabel: string;\r\n packageName: string;\r\n packageUrl: string; // e.g. \"@sparkstudio/authentication-ui\"\r\n color?: string; // optional (default black)\r\n height?: number; // optional (default 22)\r\n}\r\n\r\nexport function NpmBadge({ packageName, packageLabel, packageUrl, color = \"black\", height = 22 }: NpmBadgeProps) {\r\n const encodedLabel = encodeURIComponent(packageLabel);\r\n const badgeUrl = `https://img.shields.io/npm/v/${packageName}?logo=npm&label=${encodedLabel}&color=${color}`;\r\n const npmUrl = packageUrl;\r\n\r\n return (\r\n <a href={npmUrl} target=\"_blank\" rel=\"noopener noreferrer\">\r\n <img src={badgeUrl} alt={`${packageName} version`} style={{ height }} />\r\n </a>\r\n );\r\n}\r\n","// components/Navigation.tsx\r\nimport type { ReactNode } from \"react\";\r\n\r\ninterface NavigationProps {\r\n title?: ReactNode;\r\n children: ReactNode;\r\n isOpen: boolean;\r\n onClose: () => void;\r\n}\r\n\r\nexport function Navigation({\r\n title = \"Navigation\",\r\n children,\r\n isOpen,\r\n onClose,\r\n}: NavigationProps) {\r\n return (\r\n <>\r\n {/* Desktop sidebar */}\r\n <nav className=\"col-auto p-3 h-100 d-none d-md-flex flex-column\">\r\n <h6 className=\"text-muted text-uppercase\">{title}</h6>\r\n\r\n <ul className=\"nav nav-pills flex-column gap-2 mt-3\">\r\n {children}\r\n </ul>\r\n </nav>\r\n\r\n {/* Mobile drawer */}\r\n <div\r\n className={`offcanvas offcanvas-start d-md-none ${isOpen ? \"show\" : \"\"}`}\r\n style={{ visibility: isOpen ? \"visible\" : \"hidden\" }}\r\n tabIndex={-1}\r\n aria-hidden={isOpen ? \"false\" : \"true\"}\r\n >\r\n <div className=\"offcanvas-header\">\r\n <h5 className=\"offcanvas-title\">{title}</h5>\r\n <button className=\"btn-close\" onClick={onClose} />\r\n </div>\r\n\r\n <div className=\"offcanvas-body\">\r\n <ul className=\"nav nav-pills flex-column gap-2 mt-2\">\r\n {children}\r\n </ul>\r\n </div>\r\n </div>\r\n\r\n {/* Backdrop */}\r\n {isOpen && (\r\n <div\r\n className=\"offcanvas-backdrop fade show d-md-none\"\r\n onClick={onClose}\r\n />\r\n )}\r\n </>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface NavigationItemProps {\r\n icon: ReactNode;\r\n title: ReactNode;\r\n type: string;\r\n active?: boolean;\r\n onClick?: (type: string) => void;\r\n}\r\n\r\nexport function NavigationItem({icon, title, type, active, onClick }: NavigationItemProps) {\r\n return (\r\n <li className=\"nav-item\">\r\n <button\r\n type=\"button\"\r\n className={`nav-link text-start w-100 ${active ? \"active\" : \"\"}`}\r\n onClick={() => onClick?.(type)}\r\n >\r\n {icon} {title}\r\n </button>\r\n </li>\r\n );\r\n}\r\n","// components/NavToggleButton.tsx\r\nimport React from \"react\";\r\nimport { Button } from \"../Button\";\r\n\r\ninterface NavToggleButtonProps {\r\n onClick: () => void;\r\n label?: string;\r\n color?: \"primary\" | \"secondary\" | \"success\" | \"danger\" | \"warning\" | \"info\" | \"light\" | \"dark\";\r\n}\r\n\r\nexport const NavToggleButton: React.FC<NavToggleButtonProps> = ({\r\n onClick,\r\n label = \"Menu\",\r\n color = \"secondary\",\r\n}) => {\r\n return (\r\n <div className=\"d-md-none position-absolute top-0 end-0 m-2\">\r\n <Button\r\n variant={color}\r\n buttonClassName=\"btn btn-secondary\"\r\n onClick={() => onClick()}\r\n >\r\n ☰ {label}\r\n </Button>\r\n </div>\r\n );\r\n};\r\n","import { useEffect } from \"react\";\r\n\r\nexport type NotificationType = \"success\" | \"info\" | \"warning\" | \"danger\";\r\n\r\nexport interface Notification {\r\n id: string;\r\n message: string;\r\n type?: NotificationType;\r\n autoCloseMs?: number;\r\n}\r\n\r\ninterface NotificationsProps {\r\n notifications: Notification[];\r\n onClose: (id: string) => void;\r\n}\r\n\r\nexport function Notifications({ notifications, onClose }: NotificationsProps) {\r\n useEffect(() => {\r\n notifications.forEach((n) => {\r\n if (!n.autoCloseMs) return;\r\n\r\n const timer = setTimeout(() => onClose(n.id), n.autoCloseMs);\r\n return () => clearTimeout(timer);\r\n });\r\n }, [notifications, onClose]);\r\n\r\n return (\r\n <div\r\n className=\"position-fixed top-0 end-0\"\r\n style={{ zIndex: 1080, width: 340 }}\r\n >\r\n {notifications.map((n) => (\r\n <div\r\n key={n.id}\r\n className={`alert alert-${n.type ?? \"info\"} alert-dismissible fade show shadow-sm mb-2`}\r\n role=\"alert\"\r\n >\r\n {n.message}\r\n\r\n <button\r\n type=\"button\"\r\n className=\"btn-close\"\r\n aria-label=\"Close\"\r\n onClick={() => onClose(n.id)}\r\n />\r\n </div>\r\n ))}\r\n </div>\r\n );\r\n}\r\n","import {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useEffect,\r\n useState,\r\n type ReactNode,\r\n} from \"react\";\r\nimport type { NotificationType } from \"./Notifications\";\r\n\r\n// Rename to avoid conflict with browser Notification type\r\nexport interface AppNotification {\r\n id: string;\r\n message: string;\r\n type?: NotificationType;\r\n autoCloseMs?: number;\r\n}\r\n\r\ninterface NotificationsContextValue {\r\n push: (message: string, type?: NotificationType, autoCloseMs?: number) => void;\r\n}\r\n\r\nconst NotificationsContext = createContext<NotificationsContextValue | undefined>(\r\n undefined\r\n);\r\n\r\ninterface NotificationsProviderProps {\r\n children: ReactNode;\r\n}\r\n\r\nexport function NotificationsProvider({ children }: NotificationsProviderProps) {\r\n const [notifications, setNotifications] = useState<AppNotification[]>([]);\r\n\r\n const push = useCallback(\r\n (message: string, type: NotificationType = \"info\", autoCloseMs = 4000) => {\r\n setNotifications((prev) => [\r\n ...prev,\r\n {\r\n id: crypto.randomUUID(),\r\n message,\r\n type,\r\n autoCloseMs,\r\n },\r\n ]);\r\n },\r\n []\r\n );\r\n\r\n const close = useCallback((id: string) => {\r\n setNotifications((list) => list.filter((n) => n.id !== id));\r\n }, []);\r\n\r\n // Handle auto close timers\r\n useEffect(() => {\r\n const timers = notifications.map((n) => {\r\n if (!n.autoCloseMs) return null;\r\n return setTimeout(() => close(n.id), n.autoCloseMs);\r\n });\r\n\r\n return () => {\r\n timers.forEach((t) => t && clearTimeout(t));\r\n };\r\n }, [notifications, close]);\r\n\r\n return (\r\n <NotificationsContext.Provider value={{ push }}>\r\n {/* overlay renderer */}\r\n <div\r\n className=\"position-fixed top-0 end-0 p-3\"\r\n style={{ zIndex: 1080, width: 340 }}\r\n >\r\n {notifications.map((n) => (\r\n <div\r\n key={n.id}\r\n className={`alert alert-${n.type ?? \"info\"} alert-dismissible fade show shadow-sm mb-2`}\r\n role=\"alert\"\r\n >\r\n {n.message}\r\n <button\r\n type=\"button\"\r\n className=\"btn-close\"\r\n aria-label=\"Close\"\r\n onClick={() => close(n.id)}\r\n />\r\n </div>\r\n ))}\r\n </div>\r\n\r\n {children}\r\n </NotificationsContext.Provider>\r\n );\r\n}\r\n\r\nexport function UseNotifications(): NotificationsContextValue {\r\n const ctx = useContext(NotificationsContext);\r\n if (!ctx) {\r\n throw new Error(\"useNotifications must be used within a NotificationsProvider\");\r\n }\r\n return ctx;\r\n}\r\n","// ./views/AppsView.tsx (or ./components/AppsView.tsx)\r\nimport React from \"react\";\r\nimport type { AppItem } from \"../../models/AppItem\";\r\nimport { AppTile } from \"../AppTile\";\r\n\r\ninterface AppsViewProps {\r\n apps: AppItem[];\r\n title?: string;\r\n onAppClick?: (app: AppItem) => void;\r\n}\r\n\r\nexport const AppsView: React.FC<AppsViewProps> = ({ apps, title, onAppClick }) => {\r\n return (\r\n <>\r\n {title && <h5 className=\"mb-3\">{title}</h5>}\r\n\r\n <div className=\"row\">\r\n {apps.map((app) => (\r\n <AppTile key={app.id} app={app} onClick={onAppClick} />\r\n ))}\r\n </div>\r\n </>\r\n );\r\n};\r\n","export function ContentContainer({ children }: React.PropsWithChildren) {\r\n return <div className=\"container-fluid flex-grow-1\">{children}</div>;\r\n}\r\n","export function ContentRow({ children }: React.PropsWithChildren) {\r\n return <div className=\"row h-100 flex-nowrap\">{children}</div>;\r\n}\r\n","\r\nexport function LayoutWrapper({ children }: React.PropsWithChildren) {\r\n return (\r\n\r\n <div className=\"d-flex flex-column min-vh-100\">{children}</div>\r\n );\r\n}\r\n","// components/Modal.tsx\r\nimport React from \"react\";\r\n\r\nexport interface ModalProps {\r\n title?: string;\r\n isOpen: boolean;\r\n onClose: () => void;\r\n onConfirm?: () => void;\r\n confirmText?: string;\r\n\r\n disableBackdropClose?: boolean;\r\n disableAllButtons?: boolean;\r\n\r\n showHeaderClose?: boolean;\r\n showCloseButton?: boolean;\r\n showConfirmButton?: boolean;\r\n\r\n children?: React.ReactNode;\r\n}\r\n\r\nexport const Modal: React.FC<ModalProps> = ({\r\n title,\r\n isOpen,\r\n onClose,\r\n onConfirm,\r\n confirmText = \"OK\",\r\n\r\n disableBackdropClose = false,\r\n disableAllButtons = false,\r\n\r\n showHeaderClose = true,\r\n showCloseButton = true,\r\n showConfirmButton = true,\r\n\r\n children,\r\n}) => {\r\n if (!isOpen) return null;\r\n\r\n const handleBackdropClick = () => {\r\n if (disableBackdropClose || disableAllButtons) return;\r\n onClose();\r\n };\r\n\r\n const handleContentClick: React.MouseEventHandler<HTMLDivElement> = (e) => {\r\n e.stopPropagation();\r\n };\r\n\r\n // 👇 NEW: decide if footer should exist at all\r\n const hasFooterButtons =\r\n (showCloseButton || (onConfirm && showConfirmButton)) &&\r\n !disableAllButtons;\r\n\r\n return (\r\n <>\r\n <div\r\n className=\"modal-backdrop fade show\"\r\n style={{ zIndex: 1040 }}\r\n onClick={handleBackdropClick}\r\n />\r\n\r\n <div\r\n className=\"modal fade show d-block\"\r\n tabIndex={-1}\r\n role=\"dialog\"\r\n style={{ zIndex: 1050 }}\r\n onClick={handleBackdropClick}\r\n >\r\n <div\r\n className=\"modal-dialog modal-dialog-centered\"\r\n role=\"document\"\r\n onClick={handleContentClick}\r\n >\r\n <div className=\"modal-content shadow\">\r\n {/* Header */}\r\n <div className=\"modal-header\">\r\n {title && <h5 className=\"modal-title\">{title}</h5>}\r\n\r\n {showHeaderClose && (\r\n <button\r\n type=\"button\"\r\n className=\"btn-close\"\r\n aria-label=\"Close\"\r\n onClick={onClose}\r\n disabled={disableAllButtons}\r\n />\r\n )}\r\n </div>\r\n\r\n {/* Body */}\r\n <div className=\"modal-body\">{children}</div>\r\n\r\n {/* Footer — only if buttons are visible */}\r\n {hasFooterButtons && (\r\n <div className=\"modal-footer\">\r\n {showCloseButton && (\r\n <button\r\n type=\"button\"\r\n className=\"btn btn-secondary\"\r\n onClick={onClose}\r\n disabled={disableAllButtons}\r\n >\r\n Close\r\n </button>\r\n )}\r\n\r\n {onConfirm && showConfirmButton && (\r\n <button\r\n type=\"button\"\r\n className=\"btn btn-primary\"\r\n onClick={onConfirm}\r\n disabled={disableAllButtons}\r\n >\r\n {confirmText}\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n};\r\n","import {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useState,\r\n type ReactNode,\r\n} from \"react\";\r\nimport { Modal } from \"./Modal\";\r\n\r\nexport interface ModalOptions {\r\n title?: string;\r\n confirmText?: string;\r\n disableBackdropClose?: boolean;\r\n onConfirm?: () => void;\r\n\r\n /** extra control flags matching Modal.tsx */\r\n disableAllButtons?: boolean;\r\n showHeaderClose?: boolean;\r\n showCloseButton?: boolean;\r\n showConfirmButton?: boolean;\r\n}\r\n\r\ninterface ModalContextValue {\r\n openModal: (content: ReactNode, options?: ModalOptions) => void;\r\n closeModal: () => void;\r\n}\r\n\r\nconst ModalContext = createContext<ModalContextValue | undefined>(undefined);\r\n\r\nexport function ModalProvider({ children }: { children: ReactNode }) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [content, setContent] = useState<ReactNode>(null);\r\n const [options, setOptions] = useState<ModalOptions>({});\r\n\r\n const openModal = useCallback((content: ReactNode, options?: ModalOptions) => {\r\n setContent(content);\r\n setOptions(options ?? {});\r\n setIsOpen(true);\r\n }, []);\r\n\r\n const closeModal = useCallback(() => {\r\n setIsOpen(false);\r\n }, []);\r\n\r\n return (\r\n <ModalContext.Provider value={{ openModal, closeModal }}>\r\n {children}\r\n\r\n <Modal\r\n isOpen={isOpen}\r\n title={options.title}\r\n confirmText={options.confirmText}\r\n disableBackdropClose={options.disableBackdropClose}\r\n disableAllButtons={options.disableAllButtons}\r\n showHeaderClose={options.showHeaderClose}\r\n showCloseButton={options.showCloseButton}\r\n showConfirmButton={options.showConfirmButton}\r\n onClose={closeModal}\r\n onConfirm={options.onConfirm}\r\n >\r\n {content}\r\n </Modal>\r\n </ModalContext.Provider>\r\n );\r\n}\r\n\r\nexport function UseModal() {\r\n const ctx = useContext(ModalContext);\r\n if (!ctx) throw new Error(\"UseModal must be used within ModalProvider\");\r\n return ctx;\r\n}\r\n"],"mappings":"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","names":["import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","content","options"]}
package/dist/index.d.cts CHANGED
@@ -34,10 +34,12 @@ interface BodyProps {
34
34
  }
35
35
  declare function Body({ icon, title, description, children }: BodyProps): react_jsx_runtime.JSX.Element;
36
36
 
37
+ type ButtonVariant = "primary" | "secondary" | "success" | "danger" | "warning" | "info" | "light" | "dark" | "link";
37
38
  interface ButtonProps {
38
39
  buttonClassName?: string;
39
40
  type?: "button" | "submit" | "reset";
40
41
  disabled?: boolean;
42
+ variant?: ButtonVariant;
41
43
  onAction?: () => Promise<unknown>;
42
44
  onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
43
45
  onDone?: (result: unknown) => void;
@@ -50,7 +52,7 @@ interface ButtonProps {
50
52
  loadingText?: string;
51
53
  showSpinner?: boolean;
52
54
  }
53
- declare function Button({ buttonClassName, type, disabled, onAction, onClick, onDone, onError, onFinally, children, loadingChildren, renderContent, renderLoading, loadingText, showSpinner, }: ButtonProps): react_jsx_runtime.JSX.Element;
55
+ declare function Button({ buttonClassName, type, disabled, variant, onAction, onClick, onDone, onError, onFinally, children, loadingChildren, renderContent, renderLoading, loadingText, showSpinner, }: ButtonProps): react_jsx_runtime.JSX.Element;
54
56
 
55
57
  interface FooterProps {
56
58
  children?: ReactNode;
@@ -87,8 +89,10 @@ declare function NpmBadge({ packageName, packageLabel, packageUrl, color, height
87
89
  interface NavigationProps {
88
90
  title?: ReactNode;
89
91
  children: ReactNode;
92
+ isOpen: boolean;
93
+ onClose: () => void;
90
94
  }
91
- declare function Navigation({ title, children }: NavigationProps): react_jsx_runtime.JSX.Element;
95
+ declare function Navigation({ title, children, isOpen, onClose, }: NavigationProps): react_jsx_runtime.JSX.Element;
92
96
 
93
97
  interface NavigationItemProps {
94
98
  icon: ReactNode;
@@ -99,6 +103,13 @@ interface NavigationItemProps {
99
103
  }
100
104
  declare function NavigationItem({ icon, title, type, active, onClick }: NavigationItemProps): react_jsx_runtime.JSX.Element;
101
105
 
106
+ interface NavToggleButtonProps {
107
+ onClick: () => void;
108
+ label?: string;
109
+ color?: "primary" | "secondary" | "success" | "danger" | "warning" | "info" | "light" | "dark";
110
+ }
111
+ declare const NavToggleButton: React$1.FC<NavToggleButtonProps>;
112
+
102
113
  type NotificationType = "success" | "info" | "warning" | "danger";
103
114
  interface Notification {
104
115
  id: string;
@@ -148,7 +159,6 @@ interface ModalProps {
148
159
  confirmText?: string;
149
160
  disableBackdropClose?: boolean;
150
161
  disableAllButtons?: boolean;
151
- /** NEW: toggle visibility */
152
162
  showHeaderClose?: boolean;
153
163
  showCloseButton?: boolean;
154
164
  showConfirmButton?: boolean;
@@ -176,4 +186,4 @@ declare function ModalProvider({ children }: {
176
186
  }): react_jsx_runtime.JSX.Element;
177
187
  declare function UseModal(): ModalContextValue;
178
188
 
179
- export { Alert, type AlertVariant, type AppItem, type AppNotification, AppTile, AppsView, Body, Button, type ButtonProps, ContentContainer, ContentRow, Footer, Header, HeaderText, ImageCard, LayoutWrapper, Modal, type ModalOptions, type ModalProps, ModalProvider, Navigation, NavigationItem, type Notification, type NotificationType, Notifications, NotificationsProvider, NpmBadge, UseModal, UseNotifications };
189
+ export { Alert, type AlertVariant, type AppItem, type AppNotification, AppTile, AppsView, Body, Button, type ButtonProps, type ButtonVariant, ContentContainer, ContentRow, Footer, Header, HeaderText, ImageCard, LayoutWrapper, Modal, type ModalOptions, type ModalProps, ModalProvider, NavToggleButton, Navigation, NavigationItem, type Notification, type NotificationType, Notifications, NotificationsProvider, NpmBadge, UseModal, UseNotifications };
package/dist/index.d.ts CHANGED
@@ -34,10 +34,12 @@ interface BodyProps {
34
34
  }
35
35
  declare function Body({ icon, title, description, children }: BodyProps): react_jsx_runtime.JSX.Element;
36
36
 
37
+ type ButtonVariant = "primary" | "secondary" | "success" | "danger" | "warning" | "info" | "light" | "dark" | "link";
37
38
  interface ButtonProps {
38
39
  buttonClassName?: string;
39
40
  type?: "button" | "submit" | "reset";
40
41
  disabled?: boolean;
42
+ variant?: ButtonVariant;
41
43
  onAction?: () => Promise<unknown>;
42
44
  onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
43
45
  onDone?: (result: unknown) => void;
@@ -50,7 +52,7 @@ interface ButtonProps {
50
52
  loadingText?: string;
51
53
  showSpinner?: boolean;
52
54
  }
53
- declare function Button({ buttonClassName, type, disabled, onAction, onClick, onDone, onError, onFinally, children, loadingChildren, renderContent, renderLoading, loadingText, showSpinner, }: ButtonProps): react_jsx_runtime.JSX.Element;
55
+ declare function Button({ buttonClassName, type, disabled, variant, onAction, onClick, onDone, onError, onFinally, children, loadingChildren, renderContent, renderLoading, loadingText, showSpinner, }: ButtonProps): react_jsx_runtime.JSX.Element;
54
56
 
55
57
  interface FooterProps {
56
58
  children?: ReactNode;
@@ -87,8 +89,10 @@ declare function NpmBadge({ packageName, packageLabel, packageUrl, color, height
87
89
  interface NavigationProps {
88
90
  title?: ReactNode;
89
91
  children: ReactNode;
92
+ isOpen: boolean;
93
+ onClose: () => void;
90
94
  }
91
- declare function Navigation({ title, children }: NavigationProps): react_jsx_runtime.JSX.Element;
95
+ declare function Navigation({ title, children, isOpen, onClose, }: NavigationProps): react_jsx_runtime.JSX.Element;
92
96
 
93
97
  interface NavigationItemProps {
94
98
  icon: ReactNode;
@@ -99,6 +103,13 @@ interface NavigationItemProps {
99
103
  }
100
104
  declare function NavigationItem({ icon, title, type, active, onClick }: NavigationItemProps): react_jsx_runtime.JSX.Element;
101
105
 
106
+ interface NavToggleButtonProps {
107
+ onClick: () => void;
108
+ label?: string;
109
+ color?: "primary" | "secondary" | "success" | "danger" | "warning" | "info" | "light" | "dark";
110
+ }
111
+ declare const NavToggleButton: React$1.FC<NavToggleButtonProps>;
112
+
102
113
  type NotificationType = "success" | "info" | "warning" | "danger";
103
114
  interface Notification {
104
115
  id: string;
@@ -148,7 +159,6 @@ interface ModalProps {
148
159
  confirmText?: string;
149
160
  disableBackdropClose?: boolean;
150
161
  disableAllButtons?: boolean;
151
- /** NEW: toggle visibility */
152
162
  showHeaderClose?: boolean;
153
163
  showCloseButton?: boolean;
154
164
  showConfirmButton?: boolean;
@@ -176,4 +186,4 @@ declare function ModalProvider({ children }: {
176
186
  }): react_jsx_runtime.JSX.Element;
177
187
  declare function UseModal(): ModalContextValue;
178
188
 
179
- export { Alert, type AlertVariant, type AppItem, type AppNotification, AppTile, AppsView, Body, Button, type ButtonProps, ContentContainer, ContentRow, Footer, Header, HeaderText, ImageCard, LayoutWrapper, Modal, type ModalOptions, type ModalProps, ModalProvider, Navigation, NavigationItem, type Notification, type NotificationType, Notifications, NotificationsProvider, NpmBadge, UseModal, UseNotifications };
189
+ export { Alert, type AlertVariant, type AppItem, type AppNotification, AppTile, AppsView, Body, Button, type ButtonProps, type ButtonVariant, ContentContainer, ContentRow, Footer, Header, HeaderText, ImageCard, LayoutWrapper, Modal, type ModalOptions, type ModalProps, ModalProvider, NavToggleButton, Navigation, NavigationItem, type Notification, type NotificationType, Notifications, NotificationsProvider, NpmBadge, UseModal, UseNotifications };
package/dist/index.js CHANGED
@@ -73,6 +73,7 @@ function Button({
73
73
  buttonClassName = "btn btn-primary btn-lg",
74
74
  type = "button",
75
75
  disabled = false,
76
+ variant,
76
77
  onAction,
77
78
  onClick,
78
79
  onDone,
@@ -102,11 +103,12 @@ function Button({
102
103
  onFinally?.();
103
104
  }
104
105
  };
106
+ const baseClass = variant ? `btn btn-${variant}` : buttonClassName;
105
107
  return /* @__PURE__ */ jsx4(
106
108
  "button",
107
109
  {
108
110
  type,
109
- className: `${buttonClassName} ${loading ? "async-btn--loading" : ""}`,
111
+ className: `${baseClass} ${loading ? "async-btn--loading" : ""}`,
110
112
  onClick: handleClick,
111
113
  disabled: disabled || loading,
112
114
  children: loading ? renderLoading?.() ?? loadingChildren ?? /* @__PURE__ */ jsxs4(Fragment, { children: [
@@ -172,11 +174,41 @@ function NpmBadge({ packageName, packageLabel, packageUrl, color = "black", heig
172
174
  }
173
175
 
174
176
  // src/components/navigation/Navigation.tsx
175
- import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
176
- function Navigation({ title = "Navigation", children }) {
177
- return /* @__PURE__ */ jsxs5("nav", { className: "col-auto p-3 h-100 d-flex flex-column", children: [
178
- /* @__PURE__ */ jsx10("h6", { className: "text-muted text-uppercase", children: title }),
179
- /* @__PURE__ */ jsx10("ul", { className: "nav nav-pills flex-column gap-2 mt-3", children })
177
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
178
+ function Navigation({
179
+ title = "Navigation",
180
+ children,
181
+ isOpen,
182
+ onClose
183
+ }) {
184
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
185
+ /* @__PURE__ */ jsxs5("nav", { className: "col-auto p-3 h-100 d-none d-md-flex flex-column", children: [
186
+ /* @__PURE__ */ jsx10("h6", { className: "text-muted text-uppercase", children: title }),
187
+ /* @__PURE__ */ jsx10("ul", { className: "nav nav-pills flex-column gap-2 mt-3", children })
188
+ ] }),
189
+ /* @__PURE__ */ jsxs5(
190
+ "div",
191
+ {
192
+ className: `offcanvas offcanvas-start d-md-none ${isOpen ? "show" : ""}`,
193
+ style: { visibility: isOpen ? "visible" : "hidden" },
194
+ tabIndex: -1,
195
+ "aria-hidden": isOpen ? "false" : "true",
196
+ children: [
197
+ /* @__PURE__ */ jsxs5("div", { className: "offcanvas-header", children: [
198
+ /* @__PURE__ */ jsx10("h5", { className: "offcanvas-title", children: title }),
199
+ /* @__PURE__ */ jsx10("button", { className: "btn-close", onClick: onClose })
200
+ ] }),
201
+ /* @__PURE__ */ jsx10("div", { className: "offcanvas-body", children: /* @__PURE__ */ jsx10("ul", { className: "nav nav-pills flex-column gap-2 mt-2", children }) })
202
+ ]
203
+ }
204
+ ),
205
+ isOpen && /* @__PURE__ */ jsx10(
206
+ "div",
207
+ {
208
+ className: "offcanvas-backdrop fade show d-md-none",
209
+ onClick: onClose
210
+ }
211
+ )
180
212
  ] });
181
213
  }
182
214
 
@@ -198,9 +230,30 @@ function NavigationItem({ icon, title, type, active, onClick }) {
198
230
  ) });
199
231
  }
200
232
 
233
+ // src/components/navigation/NavToggleButton.tsx
234
+ import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
235
+ var NavToggleButton = ({
236
+ onClick,
237
+ label = "Menu",
238
+ color = "secondary"
239
+ }) => {
240
+ return /* @__PURE__ */ jsx12("div", { className: "d-md-none position-absolute top-0 end-0 m-2", children: /* @__PURE__ */ jsxs7(
241
+ Button,
242
+ {
243
+ variant: color,
244
+ buttonClassName: "btn btn-secondary",
245
+ onClick: () => onClick(),
246
+ children: [
247
+ "\u2630 ",
248
+ label
249
+ ]
250
+ }
251
+ ) });
252
+ };
253
+
201
254
  // src/components/notifications/Notifications.tsx
202
255
  import { useEffect } from "react";
203
- import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
256
+ import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
204
257
  function Notifications({ notifications, onClose }) {
205
258
  useEffect(() => {
206
259
  notifications.forEach((n) => {
@@ -209,19 +262,19 @@ function Notifications({ notifications, onClose }) {
209
262
  return () => clearTimeout(timer);
210
263
  });
211
264
  }, [notifications, onClose]);
212
- return /* @__PURE__ */ jsx12(
265
+ return /* @__PURE__ */ jsx13(
213
266
  "div",
214
267
  {
215
268
  className: "position-fixed top-0 end-0",
216
269
  style: { zIndex: 1080, width: 340 },
217
- children: notifications.map((n) => /* @__PURE__ */ jsxs7(
270
+ children: notifications.map((n) => /* @__PURE__ */ jsxs8(
218
271
  "div",
219
272
  {
220
273
  className: `alert alert-${n.type ?? "info"} alert-dismissible fade show shadow-sm mb-2`,
221
274
  role: "alert",
222
275
  children: [
223
276
  n.message,
224
- /* @__PURE__ */ jsx12(
277
+ /* @__PURE__ */ jsx13(
225
278
  "button",
226
279
  {
227
280
  type: "button",
@@ -246,7 +299,7 @@ import {
246
299
  useEffect as useEffect2,
247
300
  useState as useState2
248
301
  } from "react";
249
- import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
302
+ import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
250
303
  var NotificationsContext = createContext(
251
304
  void 0
252
305
  );
@@ -278,20 +331,20 @@ function NotificationsProvider({ children }) {
278
331
  timers.forEach((t) => t && clearTimeout(t));
279
332
  };
280
333
  }, [notifications, close]);
281
- return /* @__PURE__ */ jsxs8(NotificationsContext.Provider, { value: { push }, children: [
282
- /* @__PURE__ */ jsx13(
334
+ return /* @__PURE__ */ jsxs9(NotificationsContext.Provider, { value: { push }, children: [
335
+ /* @__PURE__ */ jsx14(
283
336
  "div",
284
337
  {
285
338
  className: "position-fixed top-0 end-0 p-3",
286
339
  style: { zIndex: 1080, width: 340 },
287
- children: notifications.map((n) => /* @__PURE__ */ jsxs8(
340
+ children: notifications.map((n) => /* @__PURE__ */ jsxs9(
288
341
  "div",
289
342
  {
290
343
  className: `alert alert-${n.type ?? "info"} alert-dismissible fade show shadow-sm mb-2`,
291
344
  role: "alert",
292
345
  children: [
293
346
  n.message,
294
- /* @__PURE__ */ jsx13(
347
+ /* @__PURE__ */ jsx14(
295
348
  "button",
296
349
  {
297
350
  type: "button",
@@ -318,34 +371,34 @@ function UseNotifications() {
318
371
  }
319
372
 
320
373
  // src/components/views/AppsView.tsx
321
- import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
374
+ import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
322
375
  var AppsView = ({ apps, title, onAppClick }) => {
323
- return /* @__PURE__ */ jsxs9(Fragment2, { children: [
324
- title && /* @__PURE__ */ jsx14("h5", { className: "mb-3", children: title }),
325
- /* @__PURE__ */ jsx14("div", { className: "row", children: apps.map((app) => /* @__PURE__ */ jsx14(AppTile, { app, onClick: onAppClick }, app.id)) })
376
+ return /* @__PURE__ */ jsxs10(Fragment3, { children: [
377
+ title && /* @__PURE__ */ jsx15("h5", { className: "mb-3", children: title }),
378
+ /* @__PURE__ */ jsx15("div", { className: "row", children: apps.map((app) => /* @__PURE__ */ jsx15(AppTile, { app, onClick: onAppClick }, app.id)) })
326
379
  ] });
327
380
  };
328
381
 
329
382
  // src/components/wrappers/ContentContainer.tsx
330
- import { jsx as jsx15 } from "react/jsx-runtime";
383
+ import { jsx as jsx16 } from "react/jsx-runtime";
331
384
  function ContentContainer({ children }) {
332
- return /* @__PURE__ */ jsx15("div", { className: "container-fluid flex-grow-1", children });
385
+ return /* @__PURE__ */ jsx16("div", { className: "container-fluid flex-grow-1", children });
333
386
  }
334
387
 
335
388
  // src/components/wrappers/ContentRow.tsx
336
- import { jsx as jsx16 } from "react/jsx-runtime";
389
+ import { jsx as jsx17 } from "react/jsx-runtime";
337
390
  function ContentRow({ children }) {
338
- return /* @__PURE__ */ jsx16("div", { className: "row h-100 flex-nowrap", children });
391
+ return /* @__PURE__ */ jsx17("div", { className: "row h-100 flex-nowrap", children });
339
392
  }
340
393
 
341
394
  // src/components/wrappers/LayoutWrapper.tsx
342
- import { jsx as jsx17 } from "react/jsx-runtime";
395
+ import { jsx as jsx18 } from "react/jsx-runtime";
343
396
  function LayoutWrapper({ children }) {
344
- return /* @__PURE__ */ jsx17("div", { className: "d-flex flex-column min-vh-100", children });
397
+ return /* @__PURE__ */ jsx18("div", { className: "d-flex flex-column min-vh-100", children });
345
398
  }
346
399
 
347
400
  // src/modal/Modal.tsx
348
- import { Fragment as Fragment3, jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
401
+ import { Fragment as Fragment4, jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
349
402
  var Modal = ({
350
403
  title,
351
404
  isOpen,
@@ -367,8 +420,9 @@ var Modal = ({
367
420
  const handleContentClick = (e) => {
368
421
  e.stopPropagation();
369
422
  };
370
- return /* @__PURE__ */ jsxs10(Fragment3, { children: [
371
- /* @__PURE__ */ jsx18(
423
+ const hasFooterButtons = (showCloseButton || onConfirm && showConfirmButton) && !disableAllButtons;
424
+ return /* @__PURE__ */ jsxs11(Fragment4, { children: [
425
+ /* @__PURE__ */ jsx19(
372
426
  "div",
373
427
  {
374
428
  className: "modal-backdrop fade show",
@@ -376,7 +430,7 @@ var Modal = ({
376
430
  onClick: handleBackdropClick
377
431
  }
378
432
  ),
379
- /* @__PURE__ */ jsx18(
433
+ /* @__PURE__ */ jsx19(
380
434
  "div",
381
435
  {
382
436
  className: "modal fade show d-block",
@@ -384,44 +438,52 @@ var Modal = ({
384
438
  role: "dialog",
385
439
  style: { zIndex: 1050 },
386
440
  onClick: handleBackdropClick,
387
- children: /* @__PURE__ */ jsx18("div", { className: "modal-dialog modal-dialog-centered", role: "document", onClick: handleContentClick, children: /* @__PURE__ */ jsxs10("div", { className: "modal-content shadow", children: [
388
- /* @__PURE__ */ jsxs10("div", { className: "modal-header", children: [
389
- title && /* @__PURE__ */ jsx18("h5", { className: "modal-title", children: title }),
390
- showHeaderClose && /* @__PURE__ */ jsx18(
391
- "button",
392
- {
393
- type: "button",
394
- className: "btn-close",
395
- "aria-label": "Close",
396
- onClick: onClose,
397
- disabled: disableAllButtons
398
- }
399
- )
400
- ] }),
401
- /* @__PURE__ */ jsx18("div", { className: "modal-body", children }),
402
- /* @__PURE__ */ jsxs10("div", { className: "modal-footer", children: [
403
- showCloseButton && /* @__PURE__ */ jsx18(
404
- "button",
405
- {
406
- type: "button",
407
- className: "btn btn-secondary",
408
- onClick: onClose,
409
- disabled: disableAllButtons,
410
- children: "Close"
411
- }
412
- ),
413
- onConfirm && showConfirmButton && /* @__PURE__ */ jsx18(
414
- "button",
415
- {
416
- type: "button",
417
- className: "btn btn-primary",
418
- onClick: onConfirm,
419
- disabled: disableAllButtons,
420
- children: confirmText
421
- }
422
- )
423
- ] })
424
- ] }) })
441
+ children: /* @__PURE__ */ jsx19(
442
+ "div",
443
+ {
444
+ className: "modal-dialog modal-dialog-centered",
445
+ role: "document",
446
+ onClick: handleContentClick,
447
+ children: /* @__PURE__ */ jsxs11("div", { className: "modal-content shadow", children: [
448
+ /* @__PURE__ */ jsxs11("div", { className: "modal-header", children: [
449
+ title && /* @__PURE__ */ jsx19("h5", { className: "modal-title", children: title }),
450
+ showHeaderClose && /* @__PURE__ */ jsx19(
451
+ "button",
452
+ {
453
+ type: "button",
454
+ className: "btn-close",
455
+ "aria-label": "Close",
456
+ onClick: onClose,
457
+ disabled: disableAllButtons
458
+ }
459
+ )
460
+ ] }),
461
+ /* @__PURE__ */ jsx19("div", { className: "modal-body", children }),
462
+ hasFooterButtons && /* @__PURE__ */ jsxs11("div", { className: "modal-footer", children: [
463
+ showCloseButton && /* @__PURE__ */ jsx19(
464
+ "button",
465
+ {
466
+ type: "button",
467
+ className: "btn btn-secondary",
468
+ onClick: onClose,
469
+ disabled: disableAllButtons,
470
+ children: "Close"
471
+ }
472
+ ),
473
+ onConfirm && showConfirmButton && /* @__PURE__ */ jsx19(
474
+ "button",
475
+ {
476
+ type: "button",
477
+ className: "btn btn-primary",
478
+ onClick: onConfirm,
479
+ disabled: disableAllButtons,
480
+ children: confirmText
481
+ }
482
+ )
483
+ ] })
484
+ ] })
485
+ }
486
+ )
425
487
  }
426
488
  )
427
489
  ] });
@@ -434,7 +496,7 @@ import {
434
496
  useContext as useContext2,
435
497
  useState as useState3
436
498
  } from "react";
437
- import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
499
+ import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
438
500
  var ModalContext = createContext2(void 0);
439
501
  function ModalProvider({ children }) {
440
502
  const [isOpen, setIsOpen] = useState3(false);
@@ -448,9 +510,9 @@ function ModalProvider({ children }) {
448
510
  const closeModal = useCallback2(() => {
449
511
  setIsOpen(false);
450
512
  }, []);
451
- return /* @__PURE__ */ jsxs11(ModalContext.Provider, { value: { openModal, closeModal }, children: [
513
+ return /* @__PURE__ */ jsxs12(ModalContext.Provider, { value: { openModal, closeModal }, children: [
452
514
  children,
453
- /* @__PURE__ */ jsx19(
515
+ /* @__PURE__ */ jsx20(
454
516
  Modal,
455
517
  {
456
518
  isOpen,
@@ -488,6 +550,7 @@ export {
488
550
  LayoutWrapper,
489
551
  Modal,
490
552
  ModalProvider,
553
+ NavToggleButton,
491
554
  Navigation,
492
555
  NavigationItem,
493
556
  Notifications,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/Alert.tsx","../src/components/AppTile.tsx","../src/components/Body.tsx","../src/components/Button.tsx","../src/components/Footer.tsx","../src/components/Header.tsx","../src/components/HeaderText.tsx","../src/components/Icon.tsx","../src/components/NpmBadge.tsx","../src/components/navigation/Navigation.tsx","../src/components/navigation/NavigationItem.tsx","../src/components/notifications/Notifications.tsx","../src/components/notifications/NotificationsProvider.tsx","../src/components/views/AppsView.tsx","../src/components/wrappers/ContentContainer.tsx","../src/components/wrappers/ContentRow.tsx","../src/components/wrappers/LayoutWrapper.tsx","../src/modal/Modal.tsx","../src/modal/ModalProvider.tsx"],"sourcesContent":["// components/Alert.tsx\r\nimport React from \"react\";\r\n\r\nexport type AlertVariant =\r\n | \"primary\"\r\n | \"secondary\"\r\n | \"success\"\r\n | \"danger\"\r\n | \"warning\"\r\n | \"info\"\r\n | \"light\"\r\n | \"dark\";\r\n\r\ninterface AlertProps {\r\n variant?: AlertVariant;\r\n title?: string;\r\n dismissible?: boolean;\r\n className?: string;\r\n children: React.ReactNode;\r\n onClose?: () => void;\r\n}\r\n\r\nexport const Alert: React.FC<AlertProps> = ({\r\n variant = \"info\",\r\n title,\r\n dismissible = false,\r\n className = \"\",\r\n children,\r\n onClose,\r\n}) => {\r\n const classes = [\r\n \"alert\",\r\n `alert-${variant}`,\r\n dismissible ? \"alert-dismissible fade show\" : \"\",\r\n className,\r\n ]\r\n .filter(Boolean)\r\n .join(\" \");\r\n\r\n return (\r\n <div role=\"alert\" className={classes}>\r\n {title && <strong className=\"d-block mb-1\">{title}</strong>}\r\n {children}\r\n\r\n {dismissible && (\r\n <button\r\n type=\"button\"\r\n className=\"btn-close\"\r\n aria-label=\"Close\"\r\n onClick={onClose}\r\n />\r\n )}\r\n </div>\r\n );\r\n};\r\n","import React from \"react\";\r\nimport type { AppItem } from \"../models/AppItem\";\r\n\r\ninterface AppTileProps {\r\n app: AppItem;\r\n onClick?: (app: AppItem) => void;\r\n}\r\n\r\nexport const AppTile: React.FC<AppTileProps> = ({ app, onClick }) => {\r\n return (\r\n <div className=\"col-auto\">\r\n <button\r\n type=\"button\"\r\n className=\"btn p-2 d-flex flex-column align-items-center border-0 bg-transparent app-icon-btn\"\r\n onClick={() => onClick?.(app)}\r\n >\r\n <div className=\"app-icon-tile d-flex align-items-center justify-content-center mb-1\">\r\n <span style={{fontSize:\"32px\"}}>{app.icon}</span>\r\n </div>\r\n\r\n <span className=\"w-100 small text-truncate\">\r\n {app.name}\r\n {app.version && <small className=\"text-muted ms-1\">v{app.version}</small>}\r\n </span>\r\n </button>\r\n </div>\r\n );\r\n};\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface BodyProps {\r\n icon: ReactNode;\r\n title: ReactNode;\r\n description?: ReactNode;\r\n children?: ReactNode;\r\n}\r\n\r\nexport function Body({ icon, title, description, children }: BodyProps) {\r\n return (\r\n\r\n <main className=\"col p-4\">\r\n <h2>{icon} {title}</h2>\r\n\r\n {description && <p>{description}</p>}\r\n {children}\r\n </main>\r\n );\r\n}\r\n","import { useState, type ReactNode, type MouseEvent } from \"react\";\r\n\r\nexport interface ButtonProps {\r\n buttonClassName?: string;\r\n type?: \"button\" | \"submit\" | \"reset\";\r\n disabled?: boolean;\r\n\r\n onAction?: () => Promise<unknown>;\r\n onClick?: (event: MouseEvent<HTMLButtonElement>) => void;\r\n onDone?: (result: unknown) => void;\r\n onError?: (error: unknown) => void;\r\n onFinally?: () => void;\r\n\r\n children?: ReactNode;\r\n loadingChildren?: ReactNode;\r\n\r\n renderContent?: () => ReactNode;\r\n renderLoading?: () => ReactNode;\r\n\r\n loadingText?: string;\r\n showSpinner?: boolean;\r\n}\r\n\r\nexport function Button({\r\n buttonClassName = \"btn btn-primary btn-lg\",\r\n type = \"button\",\r\n disabled = false,\r\n\r\n onAction,\r\n onClick,\r\n onDone,\r\n onError,\r\n onFinally,\r\n\r\n children = \"Click me\",\r\n\r\n loadingChildren,\r\n renderContent,\r\n renderLoading,\r\n\r\n loadingText = \"Loading...\",\r\n showSpinner = true,\r\n}: ButtonProps) {\r\n const [loading, setLoading] = useState(false);\r\n\r\n const handleClick = async (event: MouseEvent<HTMLButtonElement>) => {\r\n if (loading) return;\r\n\r\n onClick?.(event);\r\n if (event.defaultPrevented) return;\r\n if (!onAction) return;\r\n\r\n try {\r\n setLoading(true);\r\n const result = await onAction();\r\n onDone?.(result);\r\n } catch (error) {\r\n onError?.(error);\r\n } finally {\r\n setLoading(false);\r\n onFinally?.();\r\n }\r\n };\r\n\r\n return (\r\n <button\r\n type={type}\r\n className={`${buttonClassName} ${loading ? \"async-btn--loading\" : \"\"}`}\r\n onClick={handleClick}\r\n disabled={disabled || loading}\r\n >\r\n {loading ? (\r\n renderLoading?.() ??\r\n loadingChildren ?? (\r\n <>\r\n {showSpinner && (\r\n <span\r\n className=\"spinner-border spinner-border-sm me-2 async-btn__spinner\"\r\n role=\"status\"\r\n aria-hidden=\"true\"\r\n />\r\n )}\r\n {loadingText}\r\n </>\r\n )\r\n ) : (\r\n renderContent?.() ?? children\r\n )}\r\n </button>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface FooterProps {\r\n children?: ReactNode;\r\n}\r\n\r\nexport function Footer({ children }: FooterProps) {\r\n return (\r\n <footer className=\"bg-dark text-white text-center py-2 mt-auto\">\r\n {children ?? \"© 2025 Your Site — All rights reserved\"}\r\n </footer>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface HeaderProps {\r\n children?: ReactNode;\r\n}\r\n\r\nexport function Header({ children }: HeaderProps) {\r\n return (\r\n <nav className=\"navbar navbar-dark bg-dark px-3 sticky-top\">\r\n {children ?? (\r\n <span className=\"navbar-brand mb-0 h4\">My Website</span>\r\n )}\r\n </nav>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface HeaderBrandProps {\r\n children: ReactNode;\r\n}\r\n\r\nexport function HeaderText({ children }: HeaderBrandProps) {\r\n return (\r\n <span className=\"navbar-brand mb-0 h4\">\r\n {children}\r\n </span>\r\n );\r\n}\r\n","// ImageCard.tsx\r\nimport React from \"react\";\r\n\r\ninterface ImageCardProps {\r\n src: string;\r\n alt: string;\r\n width?: string | number;\r\n height?: string | number;\r\n}\r\n\r\nexport const ImageCard: React.FC<ImageCardProps> = ({\r\n src,\r\n alt,\r\n}) => {\r\n return (\r\n <img\r\n src={src}\r\n alt={alt}\r\n className={\"w-100\"}\r\n style={{\r\n display: \"block\",\r\n objectFit: \"cover\",\r\n }}\r\n />\r\n );\r\n};\r\n","// components/NpmBadge.tsx\r\ninterface NpmBadgeProps {\r\n packageLabel: string;\r\n packageName: string;\r\n packageUrl: string; // e.g. \"@sparkstudio/authentication-ui\"\r\n color?: string; // optional (default black)\r\n height?: number; // optional (default 22)\r\n}\r\n\r\nexport function NpmBadge({ packageName, packageLabel, packageUrl, color = \"black\", height = 22 }: NpmBadgeProps) {\r\n const encodedLabel = encodeURIComponent(packageLabel);\r\n const badgeUrl = `https://img.shields.io/npm/v/${packageName}?logo=npm&label=${encodedLabel}&color=${color}`;\r\n const npmUrl = packageUrl;\r\n\r\n return (\r\n <a href={npmUrl} target=\"_blank\" rel=\"noopener noreferrer\">\r\n <img src={badgeUrl} alt={`${packageName} version`} style={{ height }} />\r\n </a>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface NavigationProps {\r\n title?: ReactNode;\r\n children: ReactNode;\r\n}\r\n\r\nexport function Navigation({ title = \"Navigation\", children }: NavigationProps) {\r\n return (\r\n <nav className=\"col-auto p-3 h-100 d-flex flex-column\">\r\n <h6 className=\"text-muted text-uppercase\">{title}</h6>\r\n\r\n <ul className=\"nav nav-pills flex-column gap-2 mt-3\">\r\n {children}\r\n </ul>\r\n </nav>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface NavigationItemProps {\r\n icon: ReactNode;\r\n title: ReactNode;\r\n type: string;\r\n active?: boolean;\r\n onClick?: (type: string) => void;\r\n}\r\n\r\nexport function NavigationItem({icon, title, type, active, onClick }: NavigationItemProps) {\r\n return (\r\n <li className=\"nav-item\">\r\n <button\r\n type=\"button\"\r\n className={`nav-link text-start w-100 ${active ? \"active\" : \"\"}`}\r\n onClick={() => onClick?.(type)}\r\n >\r\n {icon} {title}\r\n </button>\r\n </li>\r\n );\r\n}\r\n","import { useEffect } from \"react\";\r\n\r\nexport type NotificationType = \"success\" | \"info\" | \"warning\" | \"danger\";\r\n\r\nexport interface Notification {\r\n id: string;\r\n message: string;\r\n type?: NotificationType;\r\n autoCloseMs?: number;\r\n}\r\n\r\ninterface NotificationsProps {\r\n notifications: Notification[];\r\n onClose: (id: string) => void;\r\n}\r\n\r\nexport function Notifications({ notifications, onClose }: NotificationsProps) {\r\n useEffect(() => {\r\n notifications.forEach((n) => {\r\n if (!n.autoCloseMs) return;\r\n\r\n const timer = setTimeout(() => onClose(n.id), n.autoCloseMs);\r\n return () => clearTimeout(timer);\r\n });\r\n }, [notifications, onClose]);\r\n\r\n return (\r\n <div\r\n className=\"position-fixed top-0 end-0\"\r\n style={{ zIndex: 1080, width: 340 }}\r\n >\r\n {notifications.map((n) => (\r\n <div\r\n key={n.id}\r\n className={`alert alert-${n.type ?? \"info\"} alert-dismissible fade show shadow-sm mb-2`}\r\n role=\"alert\"\r\n >\r\n {n.message}\r\n\r\n <button\r\n type=\"button\"\r\n className=\"btn-close\"\r\n aria-label=\"Close\"\r\n onClick={() => onClose(n.id)}\r\n />\r\n </div>\r\n ))}\r\n </div>\r\n );\r\n}\r\n","import {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useEffect,\r\n useState,\r\n type ReactNode,\r\n} from \"react\";\r\nimport type { NotificationType } from \"./Notifications\";\r\n\r\n// Rename to avoid conflict with browser Notification type\r\nexport interface AppNotification {\r\n id: string;\r\n message: string;\r\n type?: NotificationType;\r\n autoCloseMs?: number;\r\n}\r\n\r\ninterface NotificationsContextValue {\r\n push: (message: string, type?: NotificationType, autoCloseMs?: number) => void;\r\n}\r\n\r\nconst NotificationsContext = createContext<NotificationsContextValue | undefined>(\r\n undefined\r\n);\r\n\r\ninterface NotificationsProviderProps {\r\n children: ReactNode;\r\n}\r\n\r\nexport function NotificationsProvider({ children }: NotificationsProviderProps) {\r\n const [notifications, setNotifications] = useState<AppNotification[]>([]);\r\n\r\n const push = useCallback(\r\n (message: string, type: NotificationType = \"info\", autoCloseMs = 4000) => {\r\n setNotifications((prev) => [\r\n ...prev,\r\n {\r\n id: crypto.randomUUID(),\r\n message,\r\n type,\r\n autoCloseMs,\r\n },\r\n ]);\r\n },\r\n []\r\n );\r\n\r\n const close = useCallback((id: string) => {\r\n setNotifications((list) => list.filter((n) => n.id !== id));\r\n }, []);\r\n\r\n // Handle auto close timers\r\n useEffect(() => {\r\n const timers = notifications.map((n) => {\r\n if (!n.autoCloseMs) return null;\r\n return setTimeout(() => close(n.id), n.autoCloseMs);\r\n });\r\n\r\n return () => {\r\n timers.forEach((t) => t && clearTimeout(t));\r\n };\r\n }, [notifications, close]);\r\n\r\n return (\r\n <NotificationsContext.Provider value={{ push }}>\r\n {/* overlay renderer */}\r\n <div\r\n className=\"position-fixed top-0 end-0 p-3\"\r\n style={{ zIndex: 1080, width: 340 }}\r\n >\r\n {notifications.map((n) => (\r\n <div\r\n key={n.id}\r\n className={`alert alert-${n.type ?? \"info\"} alert-dismissible fade show shadow-sm mb-2`}\r\n role=\"alert\"\r\n >\r\n {n.message}\r\n <button\r\n type=\"button\"\r\n className=\"btn-close\"\r\n aria-label=\"Close\"\r\n onClick={() => close(n.id)}\r\n />\r\n </div>\r\n ))}\r\n </div>\r\n\r\n {children}\r\n </NotificationsContext.Provider>\r\n );\r\n}\r\n\r\nexport function UseNotifications(): NotificationsContextValue {\r\n const ctx = useContext(NotificationsContext);\r\n if (!ctx) {\r\n throw new Error(\"useNotifications must be used within a NotificationsProvider\");\r\n }\r\n return ctx;\r\n}\r\n","// ./views/AppsView.tsx (or ./components/AppsView.tsx)\r\nimport React from \"react\";\r\nimport type { AppItem } from \"../../models/AppItem\";\r\nimport { AppTile } from \"../AppTile\";\r\n\r\ninterface AppsViewProps {\r\n apps: AppItem[];\r\n title?: string;\r\n onAppClick?: (app: AppItem) => void;\r\n}\r\n\r\nexport const AppsView: React.FC<AppsViewProps> = ({ apps, title, onAppClick }) => {\r\n return (\r\n <>\r\n {title && <h5 className=\"mb-3\">{title}</h5>}\r\n\r\n <div className=\"row\">\r\n {apps.map((app) => (\r\n <AppTile key={app.id} app={app} onClick={onAppClick} />\r\n ))}\r\n </div>\r\n </>\r\n );\r\n};\r\n","export function ContentContainer({ children }: React.PropsWithChildren) {\r\n return <div className=\"container-fluid flex-grow-1\">{children}</div>;\r\n}\r\n","export function ContentRow({ children }: React.PropsWithChildren) {\r\n return <div className=\"row h-100 flex-nowrap\">{children}</div>;\r\n}\r\n","\r\nexport function LayoutWrapper({ children }: React.PropsWithChildren) {\r\n return (\r\n\r\n <div className=\"d-flex flex-column min-vh-100\">{children}</div>\r\n );\r\n}\r\n","// components/Modal.tsx\r\nimport React from \"react\";\r\n\r\nexport interface ModalProps {\r\n title?: string;\r\n isOpen: boolean;\r\n onClose: () => void;\r\n onConfirm?: () => void;\r\n confirmText?: string;\r\n\r\n disableBackdropClose?: boolean;\r\n disableAllButtons?: boolean;\r\n\r\n /** NEW: toggle visibility */\r\n showHeaderClose?: boolean;\r\n showCloseButton?: boolean;\r\n showConfirmButton?: boolean;\r\n\r\n children?: React.ReactNode;\r\n}\r\n\r\nexport const Modal: React.FC<ModalProps> = ({\r\n title,\r\n isOpen,\r\n onClose,\r\n onConfirm,\r\n confirmText = \"OK\",\r\n\r\n disableBackdropClose = false,\r\n disableAllButtons = false,\r\n\r\n showHeaderClose = true,\r\n showCloseButton = true,\r\n showConfirmButton = true,\r\n\r\n children,\r\n}) => {\r\n if (!isOpen) return null;\r\n\r\n const handleBackdropClick = () => {\r\n if (disableBackdropClose || disableAllButtons) return;\r\n onClose();\r\n };\r\n\r\n const handleContentClick: React.MouseEventHandler<HTMLDivElement> = (e) => {\r\n e.stopPropagation();\r\n };\r\n\r\n return (\r\n <>\r\n <div\r\n className=\"modal-backdrop fade show\"\r\n style={{ zIndex: 1040 }}\r\n onClick={handleBackdropClick}\r\n />\r\n\r\n <div\r\n className=\"modal fade show d-block\"\r\n tabIndex={-1}\r\n role=\"dialog\"\r\n style={{ zIndex: 1050 }}\r\n onClick={handleBackdropClick}\r\n >\r\n <div className=\"modal-dialog modal-dialog-centered\" role=\"document\" onClick={handleContentClick}>\r\n <div className=\"modal-content shadow\">\r\n {/* Header */}\r\n <div className=\"modal-header\">\r\n {title && <h5 className=\"modal-title\">{title}</h5>}\r\n\r\n {showHeaderClose && (\r\n <button\r\n type=\"button\"\r\n className=\"btn-close\"\r\n aria-label=\"Close\"\r\n onClick={onClose}\r\n disabled={disableAllButtons}\r\n />\r\n )}\r\n </div>\r\n\r\n {/* Body */}\r\n <div className=\"modal-body\">{children}</div>\r\n\r\n {/* Footer */}\r\n <div className=\"modal-footer\">\r\n {showCloseButton && (\r\n <button\r\n type=\"button\"\r\n className=\"btn btn-secondary\"\r\n onClick={onClose}\r\n disabled={disableAllButtons}\r\n >\r\n Close\r\n </button>\r\n )}\r\n\r\n {onConfirm && showConfirmButton && (\r\n <button\r\n type=\"button\"\r\n className=\"btn btn-primary\"\r\n onClick={onConfirm}\r\n disabled={disableAllButtons}\r\n >\r\n {confirmText}\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n};\r\n","import {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useState,\r\n type ReactNode,\r\n} from \"react\";\r\nimport { Modal } from \"./Modal\";\r\n\r\nexport interface ModalOptions {\r\n title?: string;\r\n confirmText?: string;\r\n disableBackdropClose?: boolean;\r\n onConfirm?: () => void;\r\n\r\n /** extra control flags matching Modal.tsx */\r\n disableAllButtons?: boolean;\r\n showHeaderClose?: boolean;\r\n showCloseButton?: boolean;\r\n showConfirmButton?: boolean;\r\n}\r\n\r\ninterface ModalContextValue {\r\n openModal: (content: ReactNode, options?: ModalOptions) => void;\r\n closeModal: () => void;\r\n}\r\n\r\nconst ModalContext = createContext<ModalContextValue | undefined>(undefined);\r\n\r\nexport function ModalProvider({ children }: { children: ReactNode }) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [content, setContent] = useState<ReactNode>(null);\r\n const [options, setOptions] = useState<ModalOptions>({});\r\n\r\n const openModal = useCallback((content: ReactNode, options?: ModalOptions) => {\r\n setContent(content);\r\n setOptions(options ?? {});\r\n setIsOpen(true);\r\n }, []);\r\n\r\n const closeModal = useCallback(() => {\r\n setIsOpen(false);\r\n }, []);\r\n\r\n return (\r\n <ModalContext.Provider value={{ openModal, closeModal }}>\r\n {children}\r\n\r\n <Modal\r\n isOpen={isOpen}\r\n title={options.title}\r\n confirmText={options.confirmText}\r\n disableBackdropClose={options.disableBackdropClose}\r\n disableAllButtons={options.disableAllButtons}\r\n showHeaderClose={options.showHeaderClose}\r\n showCloseButton={options.showCloseButton}\r\n showConfirmButton={options.showConfirmButton}\r\n onClose={closeModal}\r\n onConfirm={options.onConfirm}\r\n >\r\n {content}\r\n </Modal>\r\n </ModalContext.Provider>\r\n );\r\n}\r\n\r\nexport function UseModal() {\r\n const ctx = useContext(ModalContext);\r\n if (!ctx) throw new Error(\"UseModal must be used within ModalProvider\");\r\n return ctx;\r\n}\r\n"],"mappings":"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","names":["jsx","jsxs","jsx","jsxs","jsx","jsxs","jsx","jsx","jsx","jsx","jsx","jsx","jsxs","jsx","jsxs","jsx","jsxs","useEffect","useState","jsx","jsxs","Fragment","jsx","jsxs","jsx","jsx","jsx","Fragment","jsx","jsxs","createContext","useCallback","useContext","useState","jsx","jsxs","createContext","useState","useCallback","content","options","useContext"]}
1
+ {"version":3,"sources":["../src/components/Alert.tsx","../src/components/AppTile.tsx","../src/components/Body.tsx","../src/components/Button.tsx","../src/components/Footer.tsx","../src/components/Header.tsx","../src/components/HeaderText.tsx","../src/components/Icon.tsx","../src/components/NpmBadge.tsx","../src/components/navigation/Navigation.tsx","../src/components/navigation/NavigationItem.tsx","../src/components/navigation/NavToggleButton.tsx","../src/components/notifications/Notifications.tsx","../src/components/notifications/NotificationsProvider.tsx","../src/components/views/AppsView.tsx","../src/components/wrappers/ContentContainer.tsx","../src/components/wrappers/ContentRow.tsx","../src/components/wrappers/LayoutWrapper.tsx","../src/modal/Modal.tsx","../src/modal/ModalProvider.tsx"],"sourcesContent":["// components/Alert.tsx\r\nimport React from \"react\";\r\n\r\nexport type AlertVariant =\r\n | \"primary\"\r\n | \"secondary\"\r\n | \"success\"\r\n | \"danger\"\r\n | \"warning\"\r\n | \"info\"\r\n | \"light\"\r\n | \"dark\";\r\n\r\ninterface AlertProps {\r\n variant?: AlertVariant;\r\n title?: string;\r\n dismissible?: boolean;\r\n className?: string;\r\n children: React.ReactNode;\r\n onClose?: () => void;\r\n}\r\n\r\nexport const Alert: React.FC<AlertProps> = ({\r\n variant = \"info\",\r\n title,\r\n dismissible = false,\r\n className = \"\",\r\n children,\r\n onClose,\r\n}) => {\r\n const classes = [\r\n \"alert\",\r\n `alert-${variant}`,\r\n dismissible ? \"alert-dismissible fade show\" : \"\",\r\n className,\r\n ]\r\n .filter(Boolean)\r\n .join(\" \");\r\n\r\n return (\r\n <div role=\"alert\" className={classes}>\r\n {title && <strong className=\"d-block mb-1\">{title}</strong>}\r\n {children}\r\n\r\n {dismissible && (\r\n <button\r\n type=\"button\"\r\n className=\"btn-close\"\r\n aria-label=\"Close\"\r\n onClick={onClose}\r\n />\r\n )}\r\n </div>\r\n );\r\n};\r\n","import React from \"react\";\r\nimport type { AppItem } from \"../models/AppItem\";\r\n\r\ninterface AppTileProps {\r\n app: AppItem;\r\n onClick?: (app: AppItem) => void;\r\n}\r\n\r\nexport const AppTile: React.FC<AppTileProps> = ({ app, onClick }) => {\r\n return (\r\n <div className=\"col-auto\">\r\n <button\r\n type=\"button\"\r\n className=\"btn p-2 d-flex flex-column align-items-center border-0 bg-transparent app-icon-btn\"\r\n onClick={() => onClick?.(app)}\r\n >\r\n <div className=\"app-icon-tile d-flex align-items-center justify-content-center mb-1\">\r\n <span style={{fontSize:\"32px\"}}>{app.icon}</span>\r\n </div>\r\n\r\n <span className=\"w-100 small text-truncate\">\r\n {app.name}\r\n {app.version && <small className=\"text-muted ms-1\">v{app.version}</small>}\r\n </span>\r\n </button>\r\n </div>\r\n );\r\n};\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface BodyProps {\r\n icon: ReactNode;\r\n title: ReactNode;\r\n description?: ReactNode;\r\n children?: ReactNode;\r\n}\r\n\r\nexport function Body({ icon, title, description, children }: BodyProps) {\r\n return (\r\n\r\n <main className=\"col p-4\">\r\n <h2>{icon} {title}</h2>\r\n\r\n {description && <p>{description}</p>}\r\n {children}\r\n </main>\r\n );\r\n}\r\n","import { useState, type ReactNode, type MouseEvent } from \"react\";\r\n\r\nexport type ButtonVariant =\r\n | \"primary\"\r\n | \"secondary\"\r\n | \"success\"\r\n | \"danger\"\r\n | \"warning\"\r\n | \"info\"\r\n | \"light\"\r\n | \"dark\"\r\n | \"link\";\r\n\r\nexport interface ButtonProps {\r\n buttonClassName?: string;\r\n type?: \"button\" | \"submit\" | \"reset\";\r\n disabled?: boolean;\r\n\r\n variant?: ButtonVariant; // 👈 NEW — Bootstrap color support\r\n\r\n onAction?: () => Promise<unknown>;\r\n onClick?: (event: MouseEvent<HTMLButtonElement>) => void;\r\n onDone?: (result: unknown) => void;\r\n onError?: (error: unknown) => void;\r\n onFinally?: () => void;\r\n\r\n children?: ReactNode;\r\n loadingChildren?: ReactNode;\r\n\r\n renderContent?: () => ReactNode;\r\n renderLoading?: () => ReactNode;\r\n\r\n loadingText?: string;\r\n showSpinner?: boolean;\r\n}\r\n\r\nexport function Button({\r\n buttonClassName = \"btn btn-primary btn-lg\",\r\n type = \"button\",\r\n disabled = false,\r\n\r\n variant,\r\n\r\n onAction,\r\n onClick,\r\n onDone,\r\n onError,\r\n onFinally,\r\n\r\n children = \"Click me\",\r\n\r\n loadingChildren,\r\n renderContent,\r\n renderLoading,\r\n\r\n loadingText = \"Loading...\",\r\n showSpinner = true,\r\n}: ButtonProps) {\r\n const [loading, setLoading] = useState(false);\r\n\r\n const handleClick = async (event: MouseEvent<HTMLButtonElement>) => {\r\n if (loading) return;\r\n\r\n onClick?.(event);\r\n if (event.defaultPrevented) return;\r\n if (!onAction) return;\r\n\r\n try {\r\n setLoading(true);\r\n const result = await onAction();\r\n onDone?.(result);\r\n } catch (error) {\r\n onError?.(error);\r\n } finally {\r\n setLoading(false);\r\n onFinally?.();\r\n }\r\n };\r\n\r\n // 👇 Decide which CSS to use:\r\n // - if variant is provided, use Bootstrap \"btn btn-X\"\r\n // - otherwise fall back to user-supplied buttonClassName\r\n const baseClass = variant\r\n ? `btn btn-${variant}`\r\n : buttonClassName;\r\n\r\n return (\r\n <button\r\n type={type}\r\n className={`${baseClass} ${loading ? \"async-btn--loading\" : \"\"}`}\r\n onClick={handleClick}\r\n disabled={disabled || loading}\r\n >\r\n {loading ? (\r\n renderLoading?.() ??\r\n loadingChildren ?? (\r\n <>\r\n {showSpinner && (\r\n <span\r\n className=\"spinner-border spinner-border-sm me-2 async-btn__spinner\"\r\n role=\"status\"\r\n aria-hidden=\"true\"\r\n />\r\n )}\r\n {loadingText}\r\n </>\r\n )\r\n ) : (\r\n renderContent?.() ?? children\r\n )}\r\n </button>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface FooterProps {\r\n children?: ReactNode;\r\n}\r\n\r\nexport function Footer({ children }: FooterProps) {\r\n return (\r\n <footer className=\"bg-dark text-white text-center py-2 mt-auto\">\r\n {children ?? \"© 2025 Your Site — All rights reserved\"}\r\n </footer>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface HeaderProps {\r\n children?: ReactNode;\r\n}\r\n\r\nexport function Header({ children }: HeaderProps) {\r\n return (\r\n <nav className=\"navbar navbar-dark bg-dark px-3 sticky-top\">\r\n {children ?? (\r\n <span className=\"navbar-brand mb-0 h4\">My Website</span>\r\n )}\r\n </nav>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface HeaderBrandProps {\r\n children: ReactNode;\r\n}\r\n\r\nexport function HeaderText({ children }: HeaderBrandProps) {\r\n return (\r\n <span className=\"navbar-brand mb-0 h4\">\r\n {children}\r\n </span>\r\n );\r\n}\r\n","// ImageCard.tsx\r\nimport React from \"react\";\r\n\r\ninterface ImageCardProps {\r\n src: string;\r\n alt: string;\r\n width?: string | number;\r\n height?: string | number;\r\n}\r\n\r\nexport const ImageCard: React.FC<ImageCardProps> = ({\r\n src,\r\n alt,\r\n}) => {\r\n return (\r\n <img\r\n src={src}\r\n alt={alt}\r\n className={\"w-100\"}\r\n style={{\r\n display: \"block\",\r\n objectFit: \"cover\",\r\n }}\r\n />\r\n );\r\n};\r\n","// components/NpmBadge.tsx\r\ninterface NpmBadgeProps {\r\n packageLabel: string;\r\n packageName: string;\r\n packageUrl: string; // e.g. \"@sparkstudio/authentication-ui\"\r\n color?: string; // optional (default black)\r\n height?: number; // optional (default 22)\r\n}\r\n\r\nexport function NpmBadge({ packageName, packageLabel, packageUrl, color = \"black\", height = 22 }: NpmBadgeProps) {\r\n const encodedLabel = encodeURIComponent(packageLabel);\r\n const badgeUrl = `https://img.shields.io/npm/v/${packageName}?logo=npm&label=${encodedLabel}&color=${color}`;\r\n const npmUrl = packageUrl;\r\n\r\n return (\r\n <a href={npmUrl} target=\"_blank\" rel=\"noopener noreferrer\">\r\n <img src={badgeUrl} alt={`${packageName} version`} style={{ height }} />\r\n </a>\r\n );\r\n}\r\n","// components/Navigation.tsx\r\nimport type { ReactNode } from \"react\";\r\n\r\ninterface NavigationProps {\r\n title?: ReactNode;\r\n children: ReactNode;\r\n isOpen: boolean;\r\n onClose: () => void;\r\n}\r\n\r\nexport function Navigation({\r\n title = \"Navigation\",\r\n children,\r\n isOpen,\r\n onClose,\r\n}: NavigationProps) {\r\n return (\r\n <>\r\n {/* Desktop sidebar */}\r\n <nav className=\"col-auto p-3 h-100 d-none d-md-flex flex-column\">\r\n <h6 className=\"text-muted text-uppercase\">{title}</h6>\r\n\r\n <ul className=\"nav nav-pills flex-column gap-2 mt-3\">\r\n {children}\r\n </ul>\r\n </nav>\r\n\r\n {/* Mobile drawer */}\r\n <div\r\n className={`offcanvas offcanvas-start d-md-none ${isOpen ? \"show\" : \"\"}`}\r\n style={{ visibility: isOpen ? \"visible\" : \"hidden\" }}\r\n tabIndex={-1}\r\n aria-hidden={isOpen ? \"false\" : \"true\"}\r\n >\r\n <div className=\"offcanvas-header\">\r\n <h5 className=\"offcanvas-title\">{title}</h5>\r\n <button className=\"btn-close\" onClick={onClose} />\r\n </div>\r\n\r\n <div className=\"offcanvas-body\">\r\n <ul className=\"nav nav-pills flex-column gap-2 mt-2\">\r\n {children}\r\n </ul>\r\n </div>\r\n </div>\r\n\r\n {/* Backdrop */}\r\n {isOpen && (\r\n <div\r\n className=\"offcanvas-backdrop fade show d-md-none\"\r\n onClick={onClose}\r\n />\r\n )}\r\n </>\r\n );\r\n}\r\n","import type { ReactNode } from \"react\";\r\n\r\ninterface NavigationItemProps {\r\n icon: ReactNode;\r\n title: ReactNode;\r\n type: string;\r\n active?: boolean;\r\n onClick?: (type: string) => void;\r\n}\r\n\r\nexport function NavigationItem({icon, title, type, active, onClick }: NavigationItemProps) {\r\n return (\r\n <li className=\"nav-item\">\r\n <button\r\n type=\"button\"\r\n className={`nav-link text-start w-100 ${active ? \"active\" : \"\"}`}\r\n onClick={() => onClick?.(type)}\r\n >\r\n {icon} {title}\r\n </button>\r\n </li>\r\n );\r\n}\r\n","// components/NavToggleButton.tsx\r\nimport React from \"react\";\r\nimport { Button } from \"../Button\";\r\n\r\ninterface NavToggleButtonProps {\r\n onClick: () => void;\r\n label?: string;\r\n color?: \"primary\" | \"secondary\" | \"success\" | \"danger\" | \"warning\" | \"info\" | \"light\" | \"dark\";\r\n}\r\n\r\nexport const NavToggleButton: React.FC<NavToggleButtonProps> = ({\r\n onClick,\r\n label = \"Menu\",\r\n color = \"secondary\",\r\n}) => {\r\n return (\r\n <div className=\"d-md-none position-absolute top-0 end-0 m-2\">\r\n <Button\r\n variant={color}\r\n buttonClassName=\"btn btn-secondary\"\r\n onClick={() => onClick()}\r\n >\r\n ☰ {label}\r\n </Button>\r\n </div>\r\n );\r\n};\r\n","import { useEffect } from \"react\";\r\n\r\nexport type NotificationType = \"success\" | \"info\" | \"warning\" | \"danger\";\r\n\r\nexport interface Notification {\r\n id: string;\r\n message: string;\r\n type?: NotificationType;\r\n autoCloseMs?: number;\r\n}\r\n\r\ninterface NotificationsProps {\r\n notifications: Notification[];\r\n onClose: (id: string) => void;\r\n}\r\n\r\nexport function Notifications({ notifications, onClose }: NotificationsProps) {\r\n useEffect(() => {\r\n notifications.forEach((n) => {\r\n if (!n.autoCloseMs) return;\r\n\r\n const timer = setTimeout(() => onClose(n.id), n.autoCloseMs);\r\n return () => clearTimeout(timer);\r\n });\r\n }, [notifications, onClose]);\r\n\r\n return (\r\n <div\r\n className=\"position-fixed top-0 end-0\"\r\n style={{ zIndex: 1080, width: 340 }}\r\n >\r\n {notifications.map((n) => (\r\n <div\r\n key={n.id}\r\n className={`alert alert-${n.type ?? \"info\"} alert-dismissible fade show shadow-sm mb-2`}\r\n role=\"alert\"\r\n >\r\n {n.message}\r\n\r\n <button\r\n type=\"button\"\r\n className=\"btn-close\"\r\n aria-label=\"Close\"\r\n onClick={() => onClose(n.id)}\r\n />\r\n </div>\r\n ))}\r\n </div>\r\n );\r\n}\r\n","import {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useEffect,\r\n useState,\r\n type ReactNode,\r\n} from \"react\";\r\nimport type { NotificationType } from \"./Notifications\";\r\n\r\n// Rename to avoid conflict with browser Notification type\r\nexport interface AppNotification {\r\n id: string;\r\n message: string;\r\n type?: NotificationType;\r\n autoCloseMs?: number;\r\n}\r\n\r\ninterface NotificationsContextValue {\r\n push: (message: string, type?: NotificationType, autoCloseMs?: number) => void;\r\n}\r\n\r\nconst NotificationsContext = createContext<NotificationsContextValue | undefined>(\r\n undefined\r\n);\r\n\r\ninterface NotificationsProviderProps {\r\n children: ReactNode;\r\n}\r\n\r\nexport function NotificationsProvider({ children }: NotificationsProviderProps) {\r\n const [notifications, setNotifications] = useState<AppNotification[]>([]);\r\n\r\n const push = useCallback(\r\n (message: string, type: NotificationType = \"info\", autoCloseMs = 4000) => {\r\n setNotifications((prev) => [\r\n ...prev,\r\n {\r\n id: crypto.randomUUID(),\r\n message,\r\n type,\r\n autoCloseMs,\r\n },\r\n ]);\r\n },\r\n []\r\n );\r\n\r\n const close = useCallback((id: string) => {\r\n setNotifications((list) => list.filter((n) => n.id !== id));\r\n }, []);\r\n\r\n // Handle auto close timers\r\n useEffect(() => {\r\n const timers = notifications.map((n) => {\r\n if (!n.autoCloseMs) return null;\r\n return setTimeout(() => close(n.id), n.autoCloseMs);\r\n });\r\n\r\n return () => {\r\n timers.forEach((t) => t && clearTimeout(t));\r\n };\r\n }, [notifications, close]);\r\n\r\n return (\r\n <NotificationsContext.Provider value={{ push }}>\r\n {/* overlay renderer */}\r\n <div\r\n className=\"position-fixed top-0 end-0 p-3\"\r\n style={{ zIndex: 1080, width: 340 }}\r\n >\r\n {notifications.map((n) => (\r\n <div\r\n key={n.id}\r\n className={`alert alert-${n.type ?? \"info\"} alert-dismissible fade show shadow-sm mb-2`}\r\n role=\"alert\"\r\n >\r\n {n.message}\r\n <button\r\n type=\"button\"\r\n className=\"btn-close\"\r\n aria-label=\"Close\"\r\n onClick={() => close(n.id)}\r\n />\r\n </div>\r\n ))}\r\n </div>\r\n\r\n {children}\r\n </NotificationsContext.Provider>\r\n );\r\n}\r\n\r\nexport function UseNotifications(): NotificationsContextValue {\r\n const ctx = useContext(NotificationsContext);\r\n if (!ctx) {\r\n throw new Error(\"useNotifications must be used within a NotificationsProvider\");\r\n }\r\n return ctx;\r\n}\r\n","// ./views/AppsView.tsx (or ./components/AppsView.tsx)\r\nimport React from \"react\";\r\nimport type { AppItem } from \"../../models/AppItem\";\r\nimport { AppTile } from \"../AppTile\";\r\n\r\ninterface AppsViewProps {\r\n apps: AppItem[];\r\n title?: string;\r\n onAppClick?: (app: AppItem) => void;\r\n}\r\n\r\nexport const AppsView: React.FC<AppsViewProps> = ({ apps, title, onAppClick }) => {\r\n return (\r\n <>\r\n {title && <h5 className=\"mb-3\">{title}</h5>}\r\n\r\n <div className=\"row\">\r\n {apps.map((app) => (\r\n <AppTile key={app.id} app={app} onClick={onAppClick} />\r\n ))}\r\n </div>\r\n </>\r\n );\r\n};\r\n","export function ContentContainer({ children }: React.PropsWithChildren) {\r\n return <div className=\"container-fluid flex-grow-1\">{children}</div>;\r\n}\r\n","export function ContentRow({ children }: React.PropsWithChildren) {\r\n return <div className=\"row h-100 flex-nowrap\">{children}</div>;\r\n}\r\n","\r\nexport function LayoutWrapper({ children }: React.PropsWithChildren) {\r\n return (\r\n\r\n <div className=\"d-flex flex-column min-vh-100\">{children}</div>\r\n );\r\n}\r\n","// components/Modal.tsx\r\nimport React from \"react\";\r\n\r\nexport interface ModalProps {\r\n title?: string;\r\n isOpen: boolean;\r\n onClose: () => void;\r\n onConfirm?: () => void;\r\n confirmText?: string;\r\n\r\n disableBackdropClose?: boolean;\r\n disableAllButtons?: boolean;\r\n\r\n showHeaderClose?: boolean;\r\n showCloseButton?: boolean;\r\n showConfirmButton?: boolean;\r\n\r\n children?: React.ReactNode;\r\n}\r\n\r\nexport const Modal: React.FC<ModalProps> = ({\r\n title,\r\n isOpen,\r\n onClose,\r\n onConfirm,\r\n confirmText = \"OK\",\r\n\r\n disableBackdropClose = false,\r\n disableAllButtons = false,\r\n\r\n showHeaderClose = true,\r\n showCloseButton = true,\r\n showConfirmButton = true,\r\n\r\n children,\r\n}) => {\r\n if (!isOpen) return null;\r\n\r\n const handleBackdropClick = () => {\r\n if (disableBackdropClose || disableAllButtons) return;\r\n onClose();\r\n };\r\n\r\n const handleContentClick: React.MouseEventHandler<HTMLDivElement> = (e) => {\r\n e.stopPropagation();\r\n };\r\n\r\n // 👇 NEW: decide if footer should exist at all\r\n const hasFooterButtons =\r\n (showCloseButton || (onConfirm && showConfirmButton)) &&\r\n !disableAllButtons;\r\n\r\n return (\r\n <>\r\n <div\r\n className=\"modal-backdrop fade show\"\r\n style={{ zIndex: 1040 }}\r\n onClick={handleBackdropClick}\r\n />\r\n\r\n <div\r\n className=\"modal fade show d-block\"\r\n tabIndex={-1}\r\n role=\"dialog\"\r\n style={{ zIndex: 1050 }}\r\n onClick={handleBackdropClick}\r\n >\r\n <div\r\n className=\"modal-dialog modal-dialog-centered\"\r\n role=\"document\"\r\n onClick={handleContentClick}\r\n >\r\n <div className=\"modal-content shadow\">\r\n {/* Header */}\r\n <div className=\"modal-header\">\r\n {title && <h5 className=\"modal-title\">{title}</h5>}\r\n\r\n {showHeaderClose && (\r\n <button\r\n type=\"button\"\r\n className=\"btn-close\"\r\n aria-label=\"Close\"\r\n onClick={onClose}\r\n disabled={disableAllButtons}\r\n />\r\n )}\r\n </div>\r\n\r\n {/* Body */}\r\n <div className=\"modal-body\">{children}</div>\r\n\r\n {/* Footer — only if buttons are visible */}\r\n {hasFooterButtons && (\r\n <div className=\"modal-footer\">\r\n {showCloseButton && (\r\n <button\r\n type=\"button\"\r\n className=\"btn btn-secondary\"\r\n onClick={onClose}\r\n disabled={disableAllButtons}\r\n >\r\n Close\r\n </button>\r\n )}\r\n\r\n {onConfirm && showConfirmButton && (\r\n <button\r\n type=\"button\"\r\n className=\"btn btn-primary\"\r\n onClick={onConfirm}\r\n disabled={disableAllButtons}\r\n >\r\n {confirmText}\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n};\r\n","import {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useState,\r\n type ReactNode,\r\n} from \"react\";\r\nimport { Modal } from \"./Modal\";\r\n\r\nexport interface ModalOptions {\r\n title?: string;\r\n confirmText?: string;\r\n disableBackdropClose?: boolean;\r\n onConfirm?: () => void;\r\n\r\n /** extra control flags matching Modal.tsx */\r\n disableAllButtons?: boolean;\r\n showHeaderClose?: boolean;\r\n showCloseButton?: boolean;\r\n showConfirmButton?: boolean;\r\n}\r\n\r\ninterface ModalContextValue {\r\n openModal: (content: ReactNode, options?: ModalOptions) => void;\r\n closeModal: () => void;\r\n}\r\n\r\nconst ModalContext = createContext<ModalContextValue | undefined>(undefined);\r\n\r\nexport function ModalProvider({ children }: { children: ReactNode }) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [content, setContent] = useState<ReactNode>(null);\r\n const [options, setOptions] = useState<ModalOptions>({});\r\n\r\n const openModal = useCallback((content: ReactNode, options?: ModalOptions) => {\r\n setContent(content);\r\n setOptions(options ?? {});\r\n setIsOpen(true);\r\n }, []);\r\n\r\n const closeModal = useCallback(() => {\r\n setIsOpen(false);\r\n }, []);\r\n\r\n return (\r\n <ModalContext.Provider value={{ openModal, closeModal }}>\r\n {children}\r\n\r\n <Modal\r\n isOpen={isOpen}\r\n title={options.title}\r\n confirmText={options.confirmText}\r\n disableBackdropClose={options.disableBackdropClose}\r\n disableAllButtons={options.disableAllButtons}\r\n showHeaderClose={options.showHeaderClose}\r\n showCloseButton={options.showCloseButton}\r\n showConfirmButton={options.showConfirmButton}\r\n onClose={closeModal}\r\n onConfirm={options.onConfirm}\r\n >\r\n {content}\r\n </Modal>\r\n </ModalContext.Provider>\r\n );\r\n}\r\n\r\nexport function UseModal() {\r\n const ctx = useContext(ModalContext);\r\n if (!ctx) throw new Error(\"UseModal must be used within ModalProvider\");\r\n return ctx;\r\n}\r\n"],"mappings":"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","names":["jsx","jsxs","jsx","jsxs","jsx","jsxs","jsx","jsx","jsx","jsx","jsx","Fragment","jsx","jsxs","jsx","jsxs","jsx","jsxs","jsx","jsxs","useEffect","useState","jsx","jsxs","Fragment","jsx","jsxs","jsx","jsx","jsx","Fragment","jsx","jsxs","createContext","useCallback","useContext","useState","jsx","jsxs","createContext","useState","useCallback","content","options","useContext"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sparkstudio/common-ui",
3
- "version": "1.0.20",
3
+ "version": "1.0.22",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",