@fgv/ts-app-shell 5.1.0-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/dist/index.browser.js +3 -0
- package/dist/index.js +43 -0
- package/dist/packlets/ai-assist/index.js +6 -0
- package/dist/packlets/ai-assist/useAiAssist.js +219 -0
- package/dist/packlets/cascade/CascadeContainer.js +83 -0
- package/dist/packlets/cascade/ComparisonView.js +48 -0
- package/dist/packlets/cascade/EntityTabLayout.js +104 -0
- package/dist/packlets/cascade/MobileCascadeStack.js +63 -0
- package/dist/packlets/cascade/index.js +37 -0
- package/dist/packlets/cascade/model.js +30 -0
- package/dist/packlets/cascade/useCascadeOps.js +206 -0
- package/dist/packlets/cascade/useCascadeTransitions.js +58 -0
- package/dist/packlets/detail/DetailHelpers.js +103 -0
- package/dist/packlets/detail/index.js +6 -0
- package/dist/packlets/drop-zone/JsonDropZone.js +112 -0
- package/dist/packlets/drop-zone/index.js +6 -0
- package/dist/packlets/editing/EditFieldHelpers.js +130 -0
- package/dist/packlets/editing/MultiActionButton.js +73 -0
- package/dist/packlets/editing/NumericInput.js +119 -0
- package/dist/packlets/editing/TypeaheadInput.js +207 -0
- package/dist/packlets/editing/index.js +10 -0
- package/dist/packlets/editing/useTypeaheadMatch.js +102 -0
- package/dist/packlets/keyboard/index.js +7 -0
- package/dist/packlets/keyboard/registry.js +133 -0
- package/dist/packlets/keyboard/useKeyboardShortcuts.js +117 -0
- package/dist/packlets/messages/MessagesContext.js +76 -0
- package/dist/packlets/messages/MessagesLogger.js +103 -0
- package/dist/packlets/messages/StatusBar.js +154 -0
- package/dist/packlets/messages/Toast.js +68 -0
- package/dist/packlets/messages/index.js +11 -0
- package/dist/packlets/messages/model.js +56 -0
- package/dist/packlets/messages/useLogReporter.js +66 -0
- package/dist/packlets/modal/ConfirmDialog.js +78 -0
- package/dist/packlets/modal/Modal.js +55 -0
- package/dist/packlets/modal/index.js +7 -0
- package/dist/packlets/print/PrintEnclosure.js +60 -0
- package/dist/packlets/print/index.js +7 -0
- package/dist/packlets/print/openPrintWindow.js +112 -0
- package/dist/packlets/responsive/ResponsiveProvider.js +56 -0
- package/dist/packlets/responsive/index.js +7 -0
- package/dist/packlets/responsive/useResponsiveLayout.js +118 -0
- package/dist/packlets/selectors/EntityRow.js +276 -0
- package/dist/packlets/selectors/PreferredSelector.js +251 -0
- package/dist/packlets/selectors/index.js +24 -0
- package/dist/packlets/sidebar/CollectionSection.js +107 -0
- package/dist/packlets/sidebar/EntityList.js +164 -0
- package/dist/packlets/sidebar/FilterBar.js +42 -0
- package/dist/packlets/sidebar/FilterRow.js +182 -0
- package/dist/packlets/sidebar/GroupedEntityList.js +183 -0
- package/dist/packlets/sidebar/SearchBar.js +34 -0
- package/dist/packlets/sidebar/SidebarLayout.js +62 -0
- package/dist/packlets/sidebar/index.js +12 -0
- package/dist/packlets/theme/ThemeProvider.js +141 -0
- package/dist/packlets/theme/index.js +6 -0
- package/dist/packlets/top-bar/ModeSelector.js +46 -0
- package/dist/packlets/top-bar/TabBar.js +37 -0
- package/dist/packlets/top-bar/index.js +7 -0
- package/dist/packlets/url-sync/index.js +6 -0
- package/dist/packlets/url-sync/useUrlSync.js +157 -0
- package/eslint.config.js +22 -0
- package/lib/index.browser.d.ts +2 -0
- package/lib/index.browser.js +19 -0
- package/lib/index.d.ts +28 -0
- package/lib/index.js +59 -0
- package/lib/packlets/ai-assist/index.d.ts +6 -0
- package/lib/packlets/ai-assist/index.js +11 -0
- package/lib/packlets/ai-assist/useAiAssist.d.ts +77 -0
- package/lib/packlets/ai-assist/useAiAssist.js +223 -0
- package/lib/packlets/cascade/CascadeContainer.d.ts +44 -0
- package/lib/packlets/cascade/CascadeContainer.js +119 -0
- package/lib/packlets/cascade/ComparisonView.d.ts +35 -0
- package/lib/packlets/cascade/ComparisonView.js +54 -0
- package/lib/packlets/cascade/EntityTabLayout.d.ts +47 -0
- package/lib/packlets/cascade/EntityTabLayout.js +110 -0
- package/lib/packlets/cascade/MobileCascadeStack.d.ts +20 -0
- package/lib/packlets/cascade/MobileCascadeStack.js +99 -0
- package/lib/packlets/cascade/index.d.ts +12 -0
- package/lib/packlets/cascade/index.js +48 -0
- package/lib/packlets/cascade/model.d.ts +57 -0
- package/lib/packlets/cascade/model.js +33 -0
- package/lib/packlets/cascade/useCascadeOps.d.ts +111 -0
- package/lib/packlets/cascade/useCascadeOps.js +209 -0
- package/lib/packlets/cascade/useCascadeTransitions.d.ts +19 -0
- package/lib/packlets/cascade/useCascadeTransitions.js +62 -0
- package/lib/packlets/detail/DetailHelpers.d.ts +83 -0
- package/lib/packlets/detail/DetailHelpers.js +113 -0
- package/lib/packlets/detail/index.d.ts +6 -0
- package/lib/packlets/detail/index.js +14 -0
- package/lib/packlets/drop-zone/JsonDropZone.d.ts +40 -0
- package/lib/packlets/drop-zone/JsonDropZone.js +149 -0
- package/lib/packlets/drop-zone/index.d.ts +6 -0
- package/lib/packlets/drop-zone/index.js +10 -0
- package/lib/packlets/editing/EditFieldHelpers.d.ts +171 -0
- package/lib/packlets/editing/EditFieldHelpers.js +144 -0
- package/lib/packlets/editing/MultiActionButton.d.ts +45 -0
- package/lib/packlets/editing/MultiActionButton.js +109 -0
- package/lib/packlets/editing/NumericInput.d.ts +47 -0
- package/lib/packlets/editing/NumericInput.js +155 -0
- package/lib/packlets/editing/TypeaheadInput.d.ts +46 -0
- package/lib/packlets/editing/TypeaheadInput.js +243 -0
- package/lib/packlets/editing/index.d.ts +10 -0
- package/lib/packlets/editing/index.js +26 -0
- package/lib/packlets/editing/useTypeaheadMatch.d.ts +42 -0
- package/lib/packlets/editing/useTypeaheadMatch.js +105 -0
- package/lib/packlets/keyboard/index.d.ts +7 -0
- package/lib/packlets/keyboard/index.js +15 -0
- package/lib/packlets/keyboard/registry.d.ts +92 -0
- package/lib/packlets/keyboard/registry.js +138 -0
- package/lib/packlets/keyboard/useKeyboardShortcuts.d.ts +50 -0
- package/lib/packlets/keyboard/useKeyboardShortcuts.js +155 -0
- package/lib/packlets/messages/MessagesContext.d.ts +40 -0
- package/lib/packlets/messages/MessagesContext.js +113 -0
- package/lib/packlets/messages/MessagesLogger.d.ts +50 -0
- package/lib/packlets/messages/MessagesLogger.js +107 -0
- package/lib/packlets/messages/StatusBar.d.ts +22 -0
- package/lib/packlets/messages/StatusBar.js +190 -0
- package/lib/packlets/messages/Toast.d.ts +31 -0
- package/lib/packlets/messages/Toast.js +105 -0
- package/lib/packlets/messages/index.d.ts +11 -0
- package/lib/packlets/messages/index.js +24 -0
- package/lib/packlets/messages/model.d.ts +59 -0
- package/lib/packlets/messages/model.js +61 -0
- package/lib/packlets/messages/useLogReporter.d.ts +22 -0
- package/lib/packlets/messages/useLogReporter.js +69 -0
- package/lib/packlets/modal/ConfirmDialog.d.ts +39 -0
- package/lib/packlets/modal/ConfirmDialog.js +114 -0
- package/lib/packlets/modal/Modal.d.ts +22 -0
- package/lib/packlets/modal/Modal.js +91 -0
- package/lib/packlets/modal/index.d.ts +7 -0
- package/lib/packlets/modal/index.js +12 -0
- package/lib/packlets/print/PrintEnclosure.d.ts +33 -0
- package/lib/packlets/print/PrintEnclosure.js +96 -0
- package/lib/packlets/print/index.d.ts +7 -0
- package/lib/packlets/print/index.js +12 -0
- package/lib/packlets/print/openPrintWindow.d.ts +35 -0
- package/lib/packlets/print/openPrintWindow.js +118 -0
- package/lib/packlets/responsive/ResponsiveProvider.d.ts +35 -0
- package/lib/packlets/responsive/ResponsiveProvider.js +93 -0
- package/lib/packlets/responsive/index.d.ts +7 -0
- package/lib/packlets/responsive/index.js +13 -0
- package/lib/packlets/responsive/useResponsiveLayout.d.ts +48 -0
- package/lib/packlets/responsive/useResponsiveLayout.js +121 -0
- package/lib/packlets/selectors/EntityRow.d.ts +45 -0
- package/lib/packlets/selectors/EntityRow.js +315 -0
- package/lib/packlets/selectors/PreferredSelector.d.ts +50 -0
- package/lib/packlets/selectors/PreferredSelector.js +287 -0
- package/lib/packlets/selectors/index.d.ts +5 -0
- package/lib/packlets/selectors/index.js +29 -0
- package/lib/packlets/sidebar/CollectionSection.d.ts +82 -0
- package/lib/packlets/sidebar/CollectionSection.js +143 -0
- package/lib/packlets/sidebar/EntityList.d.ts +105 -0
- package/lib/packlets/sidebar/EntityList.js +200 -0
- package/lib/packlets/sidebar/FilterBar.d.ts +26 -0
- package/lib/packlets/sidebar/FilterBar.js +48 -0
- package/lib/packlets/sidebar/FilterRow.d.ts +42 -0
- package/lib/packlets/sidebar/FilterRow.js +218 -0
- package/lib/packlets/sidebar/GroupedEntityList.d.ts +59 -0
- package/lib/packlets/sidebar/GroupedEntityList.js +219 -0
- package/lib/packlets/sidebar/SearchBar.d.ts +19 -0
- package/lib/packlets/sidebar/SearchBar.js +40 -0
- package/lib/packlets/sidebar/SidebarLayout.d.ts +28 -0
- package/lib/packlets/sidebar/SidebarLayout.js +98 -0
- package/lib/packlets/sidebar/index.d.ts +12 -0
- package/lib/packlets/sidebar/index.js +22 -0
- package/lib/packlets/theme/ThemeProvider.d.ts +68 -0
- package/lib/packlets/theme/ThemeProvider.js +178 -0
- package/lib/packlets/theme/index.d.ts +6 -0
- package/lib/packlets/theme/index.js +11 -0
- package/lib/packlets/top-bar/ModeSelector.d.ts +38 -0
- package/lib/packlets/top-bar/ModeSelector.js +52 -0
- package/lib/packlets/top-bar/TabBar.d.ts +31 -0
- package/lib/packlets/top-bar/TabBar.js +43 -0
- package/lib/packlets/top-bar/index.d.ts +7 -0
- package/lib/packlets/top-bar/index.js +12 -0
- package/lib/packlets/url-sync/index.d.ts +6 -0
- package/lib/packlets/url-sync/index.js +12 -0
- package/lib/packlets/url-sync/useUrlSync.d.ts +75 -0
- package/lib/packlets/url-sync/useUrlSync.js +162 -0
- package/package.json +82 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2026 Erik Fortune
|
|
4
|
+
*
|
|
5
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
* in the Software without restriction, including without limitation the rights
|
|
8
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
* furnished to do so, subject to the following conditions:
|
|
11
|
+
*
|
|
12
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
* copies or substantial portions of the Software.
|
|
14
|
+
*
|
|
15
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
* SOFTWARE.
|
|
22
|
+
*/
|
|
23
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
26
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
27
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
28
|
+
}
|
|
29
|
+
Object.defineProperty(o, k2, desc);
|
|
30
|
+
}) : (function(o, m, k, k2) {
|
|
31
|
+
if (k2 === undefined) k2 = k;
|
|
32
|
+
o[k2] = m[k];
|
|
33
|
+
}));
|
|
34
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
35
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
36
|
+
}) : function(o, v) {
|
|
37
|
+
o["default"] = v;
|
|
38
|
+
});
|
|
39
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
40
|
+
var ownKeys = function(o) {
|
|
41
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
42
|
+
var ar = [];
|
|
43
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
44
|
+
return ar;
|
|
45
|
+
};
|
|
46
|
+
return ownKeys(o);
|
|
47
|
+
};
|
|
48
|
+
return function (mod) {
|
|
49
|
+
if (mod && mod.__esModule) return mod;
|
|
50
|
+
var result = {};
|
|
51
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
52
|
+
__setModuleDefault(result, mod);
|
|
53
|
+
return result;
|
|
54
|
+
};
|
|
55
|
+
})();
|
|
56
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
|
+
exports.MessagesProvider = MessagesProvider;
|
|
58
|
+
exports.useMessages = useMessages;
|
|
59
|
+
const react_1 = __importStar(require("react"));
|
|
60
|
+
const model_1 = require("./model");
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Context
|
|
63
|
+
// ============================================================================
|
|
64
|
+
const MessagesContext = react_1.default.createContext(undefined);
|
|
65
|
+
/**
|
|
66
|
+
* Provides the global message stream to the component tree.
|
|
67
|
+
* @public
|
|
68
|
+
*/
|
|
69
|
+
function MessagesProvider(props) {
|
|
70
|
+
const { maxMessages = 200, children } = props;
|
|
71
|
+
const [messages, setMessages] = (0, react_1.useState)([]);
|
|
72
|
+
const [dismissedIds, setDismissedIds] = (0, react_1.useState)(new Set());
|
|
73
|
+
const addMessage = (0, react_1.useCallback)((severity, text, action) => {
|
|
74
|
+
const msg = (0, model_1.createMessage)(severity, text, action);
|
|
75
|
+
setMessages((prev) => {
|
|
76
|
+
const next = [...prev, msg];
|
|
77
|
+
return next.length > maxMessages ? next.slice(next.length - maxMessages) : next;
|
|
78
|
+
});
|
|
79
|
+
return msg;
|
|
80
|
+
}, [maxMessages]);
|
|
81
|
+
const dismissMessage = (0, react_1.useCallback)((id) => {
|
|
82
|
+
setDismissedIds((prev) => new Set(prev).add(id));
|
|
83
|
+
}, []);
|
|
84
|
+
const clearMessages = (0, react_1.useCallback)(() => {
|
|
85
|
+
setMessages([]);
|
|
86
|
+
setDismissedIds(new Set());
|
|
87
|
+
}, []);
|
|
88
|
+
const activeToasts = (0, react_1.useMemo)(() => messages.filter((m) => !dismissedIds.has(m.id)), [messages, dismissedIds]);
|
|
89
|
+
const value = (0, react_1.useMemo)(() => ({
|
|
90
|
+
messages,
|
|
91
|
+
addMessage,
|
|
92
|
+
dismissMessage,
|
|
93
|
+
clearMessages,
|
|
94
|
+
activeToasts
|
|
95
|
+
}), [messages, addMessage, dismissMessage, clearMessages, activeToasts]);
|
|
96
|
+
return react_1.default.createElement(MessagesContext.Provider, { value: value }, children);
|
|
97
|
+
}
|
|
98
|
+
// ============================================================================
|
|
99
|
+
// Hook
|
|
100
|
+
// ============================================================================
|
|
101
|
+
/**
|
|
102
|
+
* Hook to access the messages context.
|
|
103
|
+
* Must be used within a MessagesProvider.
|
|
104
|
+
* @public
|
|
105
|
+
*/
|
|
106
|
+
function useMessages() {
|
|
107
|
+
const ctx = (0, react_1.useContext)(MessagesContext);
|
|
108
|
+
if (ctx === undefined) {
|
|
109
|
+
throw new Error('useMessages must be used within a MessagesProvider');
|
|
110
|
+
}
|
|
111
|
+
return ctx;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=MessagesContext.js.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge between the MessagesContext and the ts-utils ILogger/LogReporter system.
|
|
3
|
+
*
|
|
4
|
+
* Provides an ILogger implementation that routes log messages into the
|
|
5
|
+
* MessagesContext, enabling Result.report() integration in React components.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import { Logging, type MessageLogLevel, Success } from '@fgv/ts-utils';
|
|
10
|
+
import { type IMessageAction } from './model';
|
|
11
|
+
import { type IMessagesContextValue } from './MessagesContext';
|
|
12
|
+
/**
|
|
13
|
+
* An {@link @fgv/ts-utils#Logging.ILogger | ILogger} implementation that routes
|
|
14
|
+
* messages into a {@link IMessagesContextValue | MessagesContext}.
|
|
15
|
+
*
|
|
16
|
+
* Use this as the `logger` parameter when creating a `LogReporter` to get
|
|
17
|
+
* full `Result.report()` integration that feeds into the app's toast/log system.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const { addMessage } = useMessages();
|
|
22
|
+
* const logger = new MessagesLogger(addMessage);
|
|
23
|
+
* const reporter = new LogReporter<unknown>({ logger });
|
|
24
|
+
*
|
|
25
|
+
* // Now Result.report() flows into toasts + log panel
|
|
26
|
+
* someResult.report(reporter, { success: 'info', failure: 'error' });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
export declare class MessagesLogger extends Logging.LoggerBase {
|
|
32
|
+
private readonly _addMessage;
|
|
33
|
+
private readonly _defaultAction;
|
|
34
|
+
/**
|
|
35
|
+
* Creates a new MessagesLogger.
|
|
36
|
+
* @param addMessage - The addMessage function from MessagesContext
|
|
37
|
+
* @param logLevel - The minimum log level to display (default: 'info')
|
|
38
|
+
* @param defaultAction - Optional default action to attach to all messages
|
|
39
|
+
*/
|
|
40
|
+
constructor(addMessage: IMessagesContextValue['addMessage'], logLevel?: Logging.ReporterLogLevel, defaultAction?: IMessageAction);
|
|
41
|
+
/**
|
|
42
|
+
* Routes a formatted log message into the MessagesContext.
|
|
43
|
+
* @param message - The formatted message string
|
|
44
|
+
* @param level - The log level
|
|
45
|
+
* @returns Success with the message if it was logged, or Success with undefined if suppressed
|
|
46
|
+
* @internal
|
|
47
|
+
*/
|
|
48
|
+
protected _log(message: string, level: MessageLogLevel): Success<string | undefined>;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=MessagesLogger.d.ts.map
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2026 Erik Fortune
|
|
4
|
+
*
|
|
5
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
* in the Software without restriction, including without limitation the rights
|
|
8
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
* furnished to do so, subject to the following conditions:
|
|
11
|
+
*
|
|
12
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
* copies or substantial portions of the Software.
|
|
14
|
+
*
|
|
15
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
* SOFTWARE.
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.MessagesLogger = void 0;
|
|
25
|
+
/**
|
|
26
|
+
* Bridge between the MessagesContext and the ts-utils ILogger/LogReporter system.
|
|
27
|
+
*
|
|
28
|
+
* Provides an ILogger implementation that routes log messages into the
|
|
29
|
+
* MessagesContext, enabling Result.report() integration in React components.
|
|
30
|
+
*
|
|
31
|
+
* @packageDocumentation
|
|
32
|
+
*/
|
|
33
|
+
const ts_utils_1 = require("@fgv/ts-utils");
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// Level Mapping
|
|
36
|
+
// ============================================================================
|
|
37
|
+
/**
|
|
38
|
+
* Maps ts-utils MessageLogLevel to our MessageSeverity.
|
|
39
|
+
* `detail` maps to `'info'` (shown when logLevel is 'detail' or 'all').
|
|
40
|
+
* `quiet` is always suppressed (only shown when reporter level is 'all', handled upstream).
|
|
41
|
+
* @internal
|
|
42
|
+
*/
|
|
43
|
+
function mapLogLevel(level) {
|
|
44
|
+
switch (level) {
|
|
45
|
+
case 'detail':
|
|
46
|
+
case 'info':
|
|
47
|
+
return 'info';
|
|
48
|
+
case 'warning':
|
|
49
|
+
return 'warning';
|
|
50
|
+
case 'error':
|
|
51
|
+
return 'error';
|
|
52
|
+
case 'quiet':
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// MessagesLogger
|
|
58
|
+
// ============================================================================
|
|
59
|
+
/**
|
|
60
|
+
* An {@link @fgv/ts-utils#Logging.ILogger | ILogger} implementation that routes
|
|
61
|
+
* messages into a {@link IMessagesContextValue | MessagesContext}.
|
|
62
|
+
*
|
|
63
|
+
* Use this as the `logger` parameter when creating a `LogReporter` to get
|
|
64
|
+
* full `Result.report()` integration that feeds into the app's toast/log system.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* const { addMessage } = useMessages();
|
|
69
|
+
* const logger = new MessagesLogger(addMessage);
|
|
70
|
+
* const reporter = new LogReporter<unknown>({ logger });
|
|
71
|
+
*
|
|
72
|
+
* // Now Result.report() flows into toasts + log panel
|
|
73
|
+
* someResult.report(reporter, { success: 'info', failure: 'error' });
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* @public
|
|
77
|
+
*/
|
|
78
|
+
class MessagesLogger extends ts_utils_1.Logging.LoggerBase {
|
|
79
|
+
/**
|
|
80
|
+
* Creates a new MessagesLogger.
|
|
81
|
+
* @param addMessage - The addMessage function from MessagesContext
|
|
82
|
+
* @param logLevel - The minimum log level to display (default: 'info')
|
|
83
|
+
* @param defaultAction - Optional default action to attach to all messages
|
|
84
|
+
*/
|
|
85
|
+
constructor(addMessage, logLevel, defaultAction) {
|
|
86
|
+
super(logLevel);
|
|
87
|
+
this._addMessage = addMessage;
|
|
88
|
+
this._defaultAction = defaultAction;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Routes a formatted log message into the MessagesContext.
|
|
92
|
+
* @param message - The formatted message string
|
|
93
|
+
* @param level - The log level
|
|
94
|
+
* @returns Success with the message if it was logged, or Success with undefined if suppressed
|
|
95
|
+
* @internal
|
|
96
|
+
*/
|
|
97
|
+
_log(message, level) {
|
|
98
|
+
const severity = mapLogLevel(level);
|
|
99
|
+
if (severity !== undefined) {
|
|
100
|
+
this._addMessage(severity, message, this._defaultAction);
|
|
101
|
+
return (0, ts_utils_1.succeed)(message);
|
|
102
|
+
}
|
|
103
|
+
return (0, ts_utils_1.succeed)(undefined);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
exports.MessagesLogger = MessagesLogger;
|
|
107
|
+
//# sourceMappingURL=MessagesLogger.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Logging } from '@fgv/ts-utils';
|
|
3
|
+
import { IMessage } from './model';
|
|
4
|
+
/**
|
|
5
|
+
* Props for the StatusBar component.
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
export interface IStatusBarProps {
|
|
9
|
+
readonly messages: ReadonlyArray<IMessage>;
|
|
10
|
+
readonly onClear: () => void;
|
|
11
|
+
/** Initial filter level for the log panel. Defaults to 'all'. */
|
|
12
|
+
readonly initialFilterLevel?: Logging.ReporterLogLevel;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Collapsible status bar / log panel at the bottom of the application.
|
|
16
|
+
*
|
|
17
|
+
* Collapsed: shows severity counts.
|
|
18
|
+
* Expanded: filterable, searchable, copyable log of all messages.
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
export declare function StatusBar(props: IStatusBarProps): React.ReactElement;
|
|
22
|
+
//# sourceMappingURL=StatusBar.d.ts.map
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2026 Erik Fortune
|
|
4
|
+
*
|
|
5
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
* in the Software without restriction, including without limitation the rights
|
|
8
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
* furnished to do so, subject to the following conditions:
|
|
11
|
+
*
|
|
12
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
* copies or substantial portions of the Software.
|
|
14
|
+
*
|
|
15
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
* SOFTWARE.
|
|
22
|
+
*/
|
|
23
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
26
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
27
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
28
|
+
}
|
|
29
|
+
Object.defineProperty(o, k2, desc);
|
|
30
|
+
}) : (function(o, m, k, k2) {
|
|
31
|
+
if (k2 === undefined) k2 = k;
|
|
32
|
+
o[k2] = m[k];
|
|
33
|
+
}));
|
|
34
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
35
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
36
|
+
}) : function(o, v) {
|
|
37
|
+
o["default"] = v;
|
|
38
|
+
});
|
|
39
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
40
|
+
var ownKeys = function(o) {
|
|
41
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
42
|
+
var ar = [];
|
|
43
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
44
|
+
return ar;
|
|
45
|
+
};
|
|
46
|
+
return ownKeys(o);
|
|
47
|
+
};
|
|
48
|
+
return function (mod) {
|
|
49
|
+
if (mod && mod.__esModule) return mod;
|
|
50
|
+
var result = {};
|
|
51
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
52
|
+
__setModuleDefault(result, mod);
|
|
53
|
+
return result;
|
|
54
|
+
};
|
|
55
|
+
})();
|
|
56
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
|
+
exports.StatusBar = StatusBar;
|
|
58
|
+
const react_1 = __importStar(require("react"));
|
|
59
|
+
const ts_utils_1 = require("@fgv/ts-utils");
|
|
60
|
+
const outline_1 = require("@heroicons/react/24/outline");
|
|
61
|
+
const solid_1 = require("@heroicons/react/24/solid");
|
|
62
|
+
const responsive_1 = require("../responsive");
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// Severity display config
|
|
65
|
+
// ============================================================================
|
|
66
|
+
const SEVERITY_ICONS = {
|
|
67
|
+
info: '\u2139',
|
|
68
|
+
success: '\u2713',
|
|
69
|
+
warning: '\u26A0',
|
|
70
|
+
error: '\u2717'
|
|
71
|
+
};
|
|
72
|
+
const SEVERITY_COLORS = {
|
|
73
|
+
info: 'text-status-info-icon',
|
|
74
|
+
success: 'text-status-success-icon',
|
|
75
|
+
warning: 'text-status-warning-icon',
|
|
76
|
+
error: 'text-status-error-icon'
|
|
77
|
+
};
|
|
78
|
+
const LOG_ROW_COLORS = {
|
|
79
|
+
info: '',
|
|
80
|
+
success: 'bg-status-success-bg',
|
|
81
|
+
warning: 'bg-status-warning-bg',
|
|
82
|
+
error: 'bg-status-error-bg'
|
|
83
|
+
};
|
|
84
|
+
// ============================================================================
|
|
85
|
+
// Level filter config
|
|
86
|
+
// ============================================================================
|
|
87
|
+
/**
|
|
88
|
+
* Maps MessageSeverity to MessageLogLevel for shouldLog filtering.
|
|
89
|
+
* 'success' is treated as 'info' since it doesn't exist in the log level hierarchy.
|
|
90
|
+
*/
|
|
91
|
+
function severityToLogLevel(severity) {
|
|
92
|
+
return severity === 'success' ? 'info' : severity;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Filter choices exposed in the UI, mapping labels to ReporterLogLevel values.
|
|
96
|
+
*/
|
|
97
|
+
const FILTER_LEVELS = [
|
|
98
|
+
{ label: 'All', level: 'all' },
|
|
99
|
+
{ label: 'Info+', level: 'info' },
|
|
100
|
+
{ label: 'Warn+', level: 'warning' },
|
|
101
|
+
{ label: 'Error', level: 'error' }
|
|
102
|
+
];
|
|
103
|
+
/**
|
|
104
|
+
* Collapsible status bar / log panel at the bottom of the application.
|
|
105
|
+
*
|
|
106
|
+
* Collapsed: shows severity counts.
|
|
107
|
+
* Expanded: filterable, searchable, copyable log of all messages.
|
|
108
|
+
* @public
|
|
109
|
+
*/
|
|
110
|
+
function StatusBar(props) {
|
|
111
|
+
const { messages, onClear, initialFilterLevel } = props;
|
|
112
|
+
const { layoutMode } = (0, responsive_1.useResponsive)();
|
|
113
|
+
const isMobile = layoutMode === 'mobile';
|
|
114
|
+
const [expanded, setExpanded] = (0, react_1.useState)(false);
|
|
115
|
+
const [showFilters, setShowFilters] = (0, react_1.useState)(false);
|
|
116
|
+
const [filterLevel, setFilterLevel] = (0, react_1.useState)(initialFilterLevel !== null && initialFilterLevel !== void 0 ? initialFilterLevel : 'all');
|
|
117
|
+
const [searchTerm, setSearchTerm] = (0, react_1.useState)('');
|
|
118
|
+
const [copySuccessId, setCopySuccessId] = (0, react_1.useState)(null);
|
|
119
|
+
const counts = (0, react_1.useMemo)(() => {
|
|
120
|
+
const result = { info: 0, success: 0, warning: 0, error: 0 };
|
|
121
|
+
for (const msg of messages) {
|
|
122
|
+
result[msg.severity]++;
|
|
123
|
+
}
|
|
124
|
+
return result;
|
|
125
|
+
}, [messages]);
|
|
126
|
+
const filteredMessages = (0, react_1.useMemo)(() => {
|
|
127
|
+
return messages.filter((msg) => {
|
|
128
|
+
const passesLevel = ts_utils_1.Logging.shouldLog(severityToLogLevel(msg.severity), filterLevel);
|
|
129
|
+
const passesSearch = searchTerm === '' || msg.text.toLowerCase().includes(searchTerm.toLowerCase());
|
|
130
|
+
return passesLevel && passesSearch;
|
|
131
|
+
});
|
|
132
|
+
}, [messages, filterLevel, searchTerm]);
|
|
133
|
+
const isFiltered = filteredMessages.length !== messages.length;
|
|
134
|
+
const formatTime = (0, react_1.useCallback)((timestamp) => {
|
|
135
|
+
return new Date(timestamp).toLocaleTimeString();
|
|
136
|
+
}, []);
|
|
137
|
+
const copyToClipboard = (0, react_1.useCallback)((text, id) => {
|
|
138
|
+
navigator.clipboard
|
|
139
|
+
.writeText(text)
|
|
140
|
+
.then(() => {
|
|
141
|
+
setCopySuccessId(id);
|
|
142
|
+
setTimeout(() => setCopySuccessId(null), 2000);
|
|
143
|
+
})
|
|
144
|
+
.catch(() => undefined);
|
|
145
|
+
}, []);
|
|
146
|
+
const copyAllFiltered = (0, react_1.useCallback)(() => {
|
|
147
|
+
const text = filteredMessages
|
|
148
|
+
.map((msg) => `[${msg.severity.toUpperCase()}] ${formatTime(msg.timestamp)} - ${msg.text}`)
|
|
149
|
+
.join('\n');
|
|
150
|
+
copyToClipboard(text, '__all__');
|
|
151
|
+
}, [filteredMessages, formatTime, copyToClipboard]);
|
|
152
|
+
return (react_1.default.createElement("div", { className: "border-t border-border bg-surface" },
|
|
153
|
+
react_1.default.createElement("button", { onClick: () => setExpanded(!expanded), className: "flex items-center justify-between w-full px-4 py-1.5 text-xs text-secondary hover:bg-hover" },
|
|
154
|
+
react_1.default.createElement("div", { className: "flex items-center gap-4" },
|
|
155
|
+
Object.keys(counts).map((severity) => counts[severity] > 0 ? (react_1.default.createElement("span", { key: severity, className: `flex items-center gap-1 ${SEVERITY_COLORS[severity]}` },
|
|
156
|
+
react_1.default.createElement("span", null, SEVERITY_ICONS[severity]),
|
|
157
|
+
react_1.default.createElement("span", null, counts[severity]))) : null),
|
|
158
|
+
messages.length === 0 && react_1.default.createElement("span", { className: "text-muted" }, "No messages")),
|
|
159
|
+
react_1.default.createElement("span", { className: "text-muted" }, expanded ? '\u25BC' : '\u25B2')),
|
|
160
|
+
expanded && (react_1.default.createElement(react_1.default.Fragment, null,
|
|
161
|
+
isMobile && (react_1.default.createElement("div", { className: "fixed inset-0 z-40 bg-backdrop", onClick: () => setExpanded(false) })),
|
|
162
|
+
react_1.default.createElement("div", { className: isMobile
|
|
163
|
+
? 'fixed inset-x-0 bottom-8 z-50 flex flex-col bg-surface border-t border-border shadow-xl rounded-t-lg max-h-[70vh]'
|
|
164
|
+
: 'border-t border-border-subtle' },
|
|
165
|
+
react_1.default.createElement("div", { className: "flex items-center justify-between px-4 py-1 bg-surface-alt border-b border-border-subtle shrink-0" },
|
|
166
|
+
react_1.default.createElement("span", { className: "text-xs font-medium text-muted" }, isFiltered
|
|
167
|
+
? `${filteredMessages.length} of ${messages.length} messages`
|
|
168
|
+
: `${messages.length} message${messages.length !== 1 ? 's' : ''}`),
|
|
169
|
+
react_1.default.createElement("div", { className: "flex items-center gap-1" },
|
|
170
|
+
react_1.default.createElement("button", { onClick: () => setShowFilters(!showFilters), className: `p-1 rounded hover:bg-surface-raised ${showFilters ? 'bg-surface-raised' : ''}`, title: "Filter messages" },
|
|
171
|
+
react_1.default.createElement(outline_1.FunnelIcon, { className: "h-3.5 w-3.5 text-muted" })),
|
|
172
|
+
react_1.default.createElement("button", { onClick: copyAllFiltered, className: `p-1 rounded transition-colors ${copySuccessId === '__all__' ? 'bg-status-success-bg' : 'hover:bg-surface-raised'}`, title: copySuccessId === '__all__' ? 'Copied!' : 'Copy filtered messages' }, copySuccessId === '__all__' ? (react_1.default.createElement(solid_1.CheckIcon, { className: "h-3.5 w-3.5 text-status-success-icon" })) : (react_1.default.createElement(outline_1.DocumentDuplicateIcon, { className: "h-3.5 w-3.5 text-muted" }))),
|
|
173
|
+
react_1.default.createElement("button", { onClick: onClear, className: "text-xs text-muted hover:text-secondary ml-1" }, "Clear"))),
|
|
174
|
+
showFilters && (react_1.default.createElement("div", { className: "px-4 py-1.5 bg-surface-raised border-b border-border-subtle flex items-center gap-3 shrink-0" },
|
|
175
|
+
react_1.default.createElement("div", { className: "flex items-center gap-1" }, FILTER_LEVELS.map(({ label, level }) => (react_1.default.createElement("button", { key: level, onClick: () => setFilterLevel(level), className: `text-xs px-2 py-0.5 rounded border ${filterLevel === level
|
|
176
|
+
? 'bg-status-info-bg border-status-info-border text-status-info-text'
|
|
177
|
+
: 'bg-surface border-border text-secondary hover:bg-hover'}` }, label)))),
|
|
178
|
+
react_1.default.createElement("div", { className: "relative flex-1" },
|
|
179
|
+
react_1.default.createElement(outline_1.MagnifyingGlassIcon, { className: "absolute left-2 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted" }),
|
|
180
|
+
react_1.default.createElement("input", { type: "text", placeholder: "Search messages...", value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), className: "w-full pl-7 pr-6 py-0.5 text-xs border border-border rounded bg-surface text-primary focus:outline-none focus:ring-1 focus:ring-focus-ring focus:border-focus-ring" }),
|
|
181
|
+
searchTerm.length > 0 && (react_1.default.createElement("button", { onClick: () => setSearchTerm(''), className: "absolute right-2 top-1/2 -translate-y-1/2 text-muted hover:text-secondary text-xs", "aria-label": "Clear search" }, "\u00D7"))))),
|
|
182
|
+
react_1.default.createElement("div", { className: `overflow-y-auto ${isMobile ? 'flex-1' : 'max-h-48'}` }, filteredMessages.length === 0 ? (react_1.default.createElement("div", { className: "px-4 py-3 text-xs text-muted text-center" }, isFiltered ? 'No messages match the current filter' : 'No messages')) : (filteredMessages.map((msg) => (react_1.default.createElement("div", { key: msg.id, className: `group flex items-start gap-2 px-4 py-1.5 text-xs border-b border-border-subtle ${LOG_ROW_COLORS[msg.severity]}` },
|
|
183
|
+
react_1.default.createElement("span", { className: `shrink-0 ${SEVERITY_COLORS[msg.severity]}` }, SEVERITY_ICONS[msg.severity]),
|
|
184
|
+
react_1.default.createElement("span", { className: "flex-1 text-secondary" }, msg.text),
|
|
185
|
+
react_1.default.createElement("button", { onClick: () => copyToClipboard(msg.text, msg.id), className: `shrink-0 p-0.5 rounded transition-colors ${copySuccessId === msg.id
|
|
186
|
+
? 'opacity-100 bg-status-success-surface'
|
|
187
|
+
: 'opacity-0 group-hover:opacity-100 hover:bg-surface-raised'}`, title: copySuccessId === msg.id ? 'Copied!' : 'Copy message' }, copySuccessId === msg.id ? (react_1.default.createElement(solid_1.CheckIcon, { className: "h-3 w-3 text-status-success-icon" })) : (react_1.default.createElement(outline_1.DocumentDuplicateIcon, { className: "h-3 w-3 text-muted" }))),
|
|
188
|
+
react_1.default.createElement("span", { className: "shrink-0 text-muted" }, formatTime(msg.timestamp))))))))))));
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=StatusBar.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { IMessage } from './model';
|
|
3
|
+
/**
|
|
4
|
+
* Props for a single Toast item.
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
export interface IToastItemProps {
|
|
8
|
+
readonly message: IMessage;
|
|
9
|
+
readonly onDismiss: (id: string) => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A single toast notification.
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
export declare function ToastItem(props: IToastItemProps): React.ReactElement;
|
|
16
|
+
/**
|
|
17
|
+
* Props for the ToastContainer.
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export interface IToastContainerProps {
|
|
21
|
+
readonly toasts: ReadonlyArray<IMessage>;
|
|
22
|
+
readonly onDismiss: (id: string) => void;
|
|
23
|
+
/** Maximum number of toasts to show simultaneously */
|
|
24
|
+
readonly maxVisible?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Container that renders active toasts in the bottom-right corner.
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
export declare function ToastContainer(props: IToastContainerProps): React.ReactElement | null;
|
|
31
|
+
//# sourceMappingURL=Toast.d.ts.map
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2026 Erik Fortune
|
|
4
|
+
*
|
|
5
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
* in the Software without restriction, including without limitation the rights
|
|
8
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
* furnished to do so, subject to the following conditions:
|
|
11
|
+
*
|
|
12
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
* copies or substantial portions of the Software.
|
|
14
|
+
*
|
|
15
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
* SOFTWARE.
|
|
22
|
+
*/
|
|
23
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
26
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
27
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
28
|
+
}
|
|
29
|
+
Object.defineProperty(o, k2, desc);
|
|
30
|
+
}) : (function(o, m, k, k2) {
|
|
31
|
+
if (k2 === undefined) k2 = k;
|
|
32
|
+
o[k2] = m[k];
|
|
33
|
+
}));
|
|
34
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
35
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
36
|
+
}) : function(o, v) {
|
|
37
|
+
o["default"] = v;
|
|
38
|
+
});
|
|
39
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
40
|
+
var ownKeys = function(o) {
|
|
41
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
42
|
+
var ar = [];
|
|
43
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
44
|
+
return ar;
|
|
45
|
+
};
|
|
46
|
+
return ownKeys(o);
|
|
47
|
+
};
|
|
48
|
+
return function (mod) {
|
|
49
|
+
if (mod && mod.__esModule) return mod;
|
|
50
|
+
var result = {};
|
|
51
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
52
|
+
__setModuleDefault(result, mod);
|
|
53
|
+
return result;
|
|
54
|
+
};
|
|
55
|
+
})();
|
|
56
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
|
+
exports.ToastItem = ToastItem;
|
|
58
|
+
exports.ToastContainer = ToastContainer;
|
|
59
|
+
const react_1 = __importStar(require("react"));
|
|
60
|
+
const model_1 = require("./model");
|
|
61
|
+
const responsive_1 = require("../responsive");
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// Severity Styles
|
|
64
|
+
// ============================================================================
|
|
65
|
+
const SEVERITY_STYLES = {
|
|
66
|
+
info: 'bg-status-info-bg border-status-info-border text-status-info-text',
|
|
67
|
+
success: 'bg-status-success-bg border-status-success-border text-status-success-text',
|
|
68
|
+
warning: 'bg-status-warning-bg border-status-warning-border text-status-warning-text',
|
|
69
|
+
error: 'bg-status-error-bg border-status-error-border text-status-error-text'
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* A single toast notification.
|
|
73
|
+
* @public
|
|
74
|
+
*/
|
|
75
|
+
function ToastItem(props) {
|
|
76
|
+
const { message, onDismiss } = props;
|
|
77
|
+
const config = model_1.DEFAULT_TOAST_CONFIG[message.severity];
|
|
78
|
+
const { layoutMode } = (0, responsive_1.useResponsive)();
|
|
79
|
+
(0, react_1.useEffect)(() => {
|
|
80
|
+
if (config.autoDismissMs > 0) {
|
|
81
|
+
const timer = setTimeout(() => onDismiss(message.id), config.autoDismissMs);
|
|
82
|
+
return () => clearTimeout(timer);
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}, [message.id, config.autoDismissMs, onDismiss]);
|
|
86
|
+
return (react_1.default.createElement("div", { className: `flex items-start gap-3 px-4 py-3 rounded-lg border shadow-lg ${layoutMode === 'mobile' ? 'w-full' : 'max-w-sm'} ${SEVERITY_STYLES[message.severity]}`, role: "alert" },
|
|
87
|
+
react_1.default.createElement("div", { className: "flex-1 text-sm line-clamp-4", title: message.text }, message.text),
|
|
88
|
+
react_1.default.createElement("div", { className: "flex items-center gap-2 shrink-0" },
|
|
89
|
+
message.action && (react_1.default.createElement("button", { onClick: message.action.onAction, className: "text-sm font-medium underline hover:no-underline" }, message.action.label)),
|
|
90
|
+
react_1.default.createElement("button", { onClick: () => onDismiss(message.id), className: "text-current opacity-50 hover:opacity-100", "aria-label": "Dismiss" }, "\u00D7"))));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Container that renders active toasts in the bottom-right corner.
|
|
94
|
+
* @public
|
|
95
|
+
*/
|
|
96
|
+
function ToastContainer(props) {
|
|
97
|
+
const { toasts, onDismiss, maxVisible = 5 } = props;
|
|
98
|
+
const { layoutMode } = (0, responsive_1.useResponsive)();
|
|
99
|
+
const visible = toasts.slice(-maxVisible);
|
|
100
|
+
if (visible.length === 0) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
return (react_1.default.createElement("div", { className: `fixed bottom-16 z-50 flex flex-col gap-2 ${layoutMode === 'mobile' ? 'left-4 right-4' : 'right-4'}` }, visible.map((toast) => (react_1.default.createElement(ToastItem, { key: toast.id, message: toast, onDismiss: onDismiss })))));
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=Toast.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Messages packlet - observability context, toast system, and log panel.
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
*/
|
|
5
|
+
export { type MessageSeverity, type IMessage, type IMessageAction, type IToastConfig, DEFAULT_TOAST_CONFIG, createMessage, generateMessageId } from './model';
|
|
6
|
+
export { type IMessagesContextValue, type IMessagesProviderProps, MessagesProvider, useMessages } from './MessagesContext';
|
|
7
|
+
export { ToastItem, ToastContainer, type IToastItemProps, type IToastContainerProps } from './Toast';
|
|
8
|
+
export { StatusBar, type IStatusBarProps } from './StatusBar';
|
|
9
|
+
export { MessagesLogger } from './MessagesLogger';
|
|
10
|
+
export { useLogReporter, type IUseLogReporterOptions } from './useLogReporter';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Messages packlet - observability context, toast system, and log panel.
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.useLogReporter = exports.MessagesLogger = exports.StatusBar = exports.ToastContainer = exports.ToastItem = exports.useMessages = exports.MessagesProvider = exports.generateMessageId = exports.createMessage = exports.DEFAULT_TOAST_CONFIG = void 0;
|
|
8
|
+
var model_1 = require("./model");
|
|
9
|
+
Object.defineProperty(exports, "DEFAULT_TOAST_CONFIG", { enumerable: true, get: function () { return model_1.DEFAULT_TOAST_CONFIG; } });
|
|
10
|
+
Object.defineProperty(exports, "createMessage", { enumerable: true, get: function () { return model_1.createMessage; } });
|
|
11
|
+
Object.defineProperty(exports, "generateMessageId", { enumerable: true, get: function () { return model_1.generateMessageId; } });
|
|
12
|
+
var MessagesContext_1 = require("./MessagesContext");
|
|
13
|
+
Object.defineProperty(exports, "MessagesProvider", { enumerable: true, get: function () { return MessagesContext_1.MessagesProvider; } });
|
|
14
|
+
Object.defineProperty(exports, "useMessages", { enumerable: true, get: function () { return MessagesContext_1.useMessages; } });
|
|
15
|
+
var Toast_1 = require("./Toast");
|
|
16
|
+
Object.defineProperty(exports, "ToastItem", { enumerable: true, get: function () { return Toast_1.ToastItem; } });
|
|
17
|
+
Object.defineProperty(exports, "ToastContainer", { enumerable: true, get: function () { return Toast_1.ToastContainer; } });
|
|
18
|
+
var StatusBar_1 = require("./StatusBar");
|
|
19
|
+
Object.defineProperty(exports, "StatusBar", { enumerable: true, get: function () { return StatusBar_1.StatusBar; } });
|
|
20
|
+
var MessagesLogger_1 = require("./MessagesLogger");
|
|
21
|
+
Object.defineProperty(exports, "MessagesLogger", { enumerable: true, get: function () { return MessagesLogger_1.MessagesLogger; } });
|
|
22
|
+
var useLogReporter_1 = require("./useLogReporter");
|
|
23
|
+
Object.defineProperty(exports, "useLogReporter", { enumerable: true, get: function () { return useLogReporter_1.useLogReporter; } });
|
|
24
|
+
//# sourceMappingURL=index.js.map
|