@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,149 @@
|
|
|
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.stripCodeFences = stripCodeFences;
|
|
58
|
+
exports.JsonDropZone = JsonDropZone;
|
|
59
|
+
const react_1 = __importStar(require("react"));
|
|
60
|
+
/**
|
|
61
|
+
* Strips markdown code fences from text.
|
|
62
|
+
* AI agents often wrap JSON in code fences.
|
|
63
|
+
* @param text - Raw text that may contain code fences
|
|
64
|
+
* @returns Text with outer code fences removed
|
|
65
|
+
* @internal
|
|
66
|
+
*/
|
|
67
|
+
function stripCodeFences(text) {
|
|
68
|
+
const trimmed = text.trim();
|
|
69
|
+
const fencePattern = /^```(?:\w+)?\s*\n?([\s\S]*?)\n?\s*```$/;
|
|
70
|
+
const match = fencePattern.exec(trimmed);
|
|
71
|
+
return match ? match[1].trim() : trimmed;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Generic JSON drop/paste target with converter-based validation.
|
|
75
|
+
* Accepts text via drag-and-drop or paste, strips markdown code fences,
|
|
76
|
+
* parses as JSON, validates through the provided converter, and calls
|
|
77
|
+
* the appropriate callback.
|
|
78
|
+
*
|
|
79
|
+
* @typeParam T - The expected validated type
|
|
80
|
+
* @public
|
|
81
|
+
*/
|
|
82
|
+
function JsonDropZone(props) {
|
|
83
|
+
const { converter, onValueReceived, onError, error, hint, className } = props;
|
|
84
|
+
const [isDragOver, setIsDragOver] = (0, react_1.useState)(false);
|
|
85
|
+
const [internalError, setInternalError] = (0, react_1.useState)(undefined);
|
|
86
|
+
const displayError = error !== null && error !== void 0 ? error : internalError;
|
|
87
|
+
const processText = (0, react_1.useCallback)((rawText) => {
|
|
88
|
+
setInternalError(undefined);
|
|
89
|
+
const stripped = stripCodeFences(rawText);
|
|
90
|
+
let parsed;
|
|
91
|
+
try {
|
|
92
|
+
parsed = JSON.parse(stripped);
|
|
93
|
+
}
|
|
94
|
+
catch (_a) {
|
|
95
|
+
const message = 'Invalid JSON: could not parse the dropped text.';
|
|
96
|
+
setInternalError(message);
|
|
97
|
+
onError === null || onError === void 0 ? void 0 : onError(message);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const result = converter.convert(parsed);
|
|
101
|
+
if (result.isSuccess()) {
|
|
102
|
+
setInternalError(undefined);
|
|
103
|
+
onValueReceived(result.value);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
const message = `Validation failed: ${result.message}`;
|
|
107
|
+
setInternalError(message);
|
|
108
|
+
onError === null || onError === void 0 ? void 0 : onError(message);
|
|
109
|
+
}
|
|
110
|
+
}, [converter, onValueReceived, onError]);
|
|
111
|
+
const handleDragOver = (0, react_1.useCallback)((e) => {
|
|
112
|
+
e.preventDefault();
|
|
113
|
+
e.stopPropagation();
|
|
114
|
+
setIsDragOver(true);
|
|
115
|
+
}, []);
|
|
116
|
+
const handleDragLeave = (0, react_1.useCallback)((e) => {
|
|
117
|
+
e.preventDefault();
|
|
118
|
+
e.stopPropagation();
|
|
119
|
+
setIsDragOver(false);
|
|
120
|
+
}, []);
|
|
121
|
+
const handleDrop = (0, react_1.useCallback)((e) => {
|
|
122
|
+
e.preventDefault();
|
|
123
|
+
e.stopPropagation();
|
|
124
|
+
setIsDragOver(false);
|
|
125
|
+
const text = e.dataTransfer.getData('text/plain') || e.dataTransfer.getData('text');
|
|
126
|
+
if (text) {
|
|
127
|
+
processText(text);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const message = 'No text data found in the dropped content.';
|
|
131
|
+
setInternalError(message);
|
|
132
|
+
onError === null || onError === void 0 ? void 0 : onError(message);
|
|
133
|
+
}
|
|
134
|
+
}, [processText, onError]);
|
|
135
|
+
const handlePaste = (0, react_1.useCallback)((e) => {
|
|
136
|
+
e.preventDefault();
|
|
137
|
+
const text = e.clipboardData.getData('text/plain') || e.clipboardData.getData('text');
|
|
138
|
+
if (text) {
|
|
139
|
+
processText(text);
|
|
140
|
+
}
|
|
141
|
+
}, [processText]);
|
|
142
|
+
const borderClass = displayError
|
|
143
|
+
? 'border-status-error-border bg-status-error-bg'
|
|
144
|
+
: isDragOver
|
|
145
|
+
? 'border-status-info-border bg-status-info-bg'
|
|
146
|
+
: 'border-border bg-surface-alt';
|
|
147
|
+
return (react_1.default.createElement("div", { className: `border-2 border-dashed rounded-md p-3 text-center text-sm transition-colors cursor-default ${borderClass} ${className !== null && className !== void 0 ? className : ''}`, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, onPaste: handlePaste, tabIndex: 0, role: "region", "aria-label": hint !== null && hint !== void 0 ? hint : 'Drop or paste JSON here' }, displayError ? (react_1.default.createElement("span", { className: "text-status-error-text text-xs" }, displayError)) : (react_1.default.createElement("span", { className: "text-muted" }, hint !== null && hint !== void 0 ? hint : 'Drop or paste JSON here'))));
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=JsonDropZone.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Drop zone packlet - generic JSON drop/paste target with converter-based validation.
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.JsonDropZone = void 0;
|
|
8
|
+
var JsonDropZone_1 = require("./JsonDropZone");
|
|
9
|
+
Object.defineProperty(exports, "JsonDropZone", { enumerable: true, get: function () { return JsonDropZone_1.JsonDropZone; } });
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic edit field helper components for entity editors.
|
|
3
|
+
*
|
|
4
|
+
* Provides reusable form field primitives: text inputs, number inputs,
|
|
5
|
+
* select dropdowns, tag editors, checkboxes, and layout wrappers.
|
|
6
|
+
* These components are domain-agnostic and can be used by any entity editor.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
import React from 'react';
|
|
11
|
+
/**
|
|
12
|
+
* Props for the EditField component.
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
export interface IEditFieldProps {
|
|
16
|
+
/** Label text displayed to the left of the field */
|
|
17
|
+
readonly label: string;
|
|
18
|
+
/** The input control(s) to render */
|
|
19
|
+
readonly children: React.ReactNode;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Horizontal label + field layout for a single edit field.
|
|
23
|
+
* @public
|
|
24
|
+
*/
|
|
25
|
+
export declare function EditField({ label, children }: IEditFieldProps): React.ReactElement;
|
|
26
|
+
/**
|
|
27
|
+
* Props for the EditSection component.
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
export interface IEditSectionProps {
|
|
31
|
+
/** Section heading text */
|
|
32
|
+
readonly title: string;
|
|
33
|
+
/** Section content (typically EditField components) */
|
|
34
|
+
readonly children: React.ReactNode;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Titled section wrapper for grouping related edit fields.
|
|
38
|
+
* @public
|
|
39
|
+
*/
|
|
40
|
+
export declare function EditSection({ title, children }: IEditSectionProps): React.ReactElement;
|
|
41
|
+
/**
|
|
42
|
+
* Props for the TextInput component.
|
|
43
|
+
* @public
|
|
44
|
+
*/
|
|
45
|
+
export interface ITextInputProps {
|
|
46
|
+
/** Current value */
|
|
47
|
+
readonly value: string;
|
|
48
|
+
/** Called when value changes */
|
|
49
|
+
readonly onChange: (value: string) => void;
|
|
50
|
+
/** Placeholder text */
|
|
51
|
+
readonly placeholder?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Single-line text input for required string fields.
|
|
55
|
+
* @public
|
|
56
|
+
*/
|
|
57
|
+
export declare function TextInput({ value, onChange, placeholder }: ITextInputProps): React.ReactElement;
|
|
58
|
+
/**
|
|
59
|
+
* Props for the OptionalTextInput component.
|
|
60
|
+
* @public
|
|
61
|
+
*/
|
|
62
|
+
export interface IOptionalTextInputProps {
|
|
63
|
+
/** Current value (undefined means empty) */
|
|
64
|
+
readonly value: string | undefined;
|
|
65
|
+
/** Called when value changes (empty string → undefined) */
|
|
66
|
+
readonly onChange: (value: string | undefined) => void;
|
|
67
|
+
/** Placeholder text */
|
|
68
|
+
readonly placeholder?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Single-line text input for optional string fields.
|
|
72
|
+
* Empty input produces `undefined`.
|
|
73
|
+
* @public
|
|
74
|
+
*/
|
|
75
|
+
export declare function OptionalTextInput({ value, onChange, placeholder }: IOptionalTextInputProps): React.ReactElement;
|
|
76
|
+
/**
|
|
77
|
+
* Props for the TextAreaInput component.
|
|
78
|
+
* @public
|
|
79
|
+
*/
|
|
80
|
+
export interface ITextAreaInputProps {
|
|
81
|
+
/** Current value (undefined means empty) */
|
|
82
|
+
readonly value: string | undefined;
|
|
83
|
+
/** Called when value changes (empty string → undefined) */
|
|
84
|
+
readonly onChange: (value: string | undefined) => void;
|
|
85
|
+
/** Placeholder text */
|
|
86
|
+
readonly placeholder?: string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Multi-line text input for optional string fields.
|
|
90
|
+
* Empty input produces `undefined`.
|
|
91
|
+
* @public
|
|
92
|
+
*/
|
|
93
|
+
export declare function TextAreaInput({ value, onChange, placeholder }: ITextAreaInputProps): React.ReactElement;
|
|
94
|
+
/**
|
|
95
|
+
* Props for the NumberInput component.
|
|
96
|
+
* @public
|
|
97
|
+
*/
|
|
98
|
+
export interface INumberInputProps {
|
|
99
|
+
/** Current value (undefined means empty) */
|
|
100
|
+
readonly value: number | undefined;
|
|
101
|
+
/** Called when value changes (empty input → undefined) */
|
|
102
|
+
readonly onChange: (value: number | undefined) => void;
|
|
103
|
+
/** Accessible label for the input */
|
|
104
|
+
readonly label: string;
|
|
105
|
+
/** Minimum allowed value */
|
|
106
|
+
readonly min?: number;
|
|
107
|
+
/** Maximum allowed value */
|
|
108
|
+
readonly max?: number;
|
|
109
|
+
/** Step increment */
|
|
110
|
+
readonly step?: number;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Numeric input for optional number fields.
|
|
114
|
+
* Empty input produces `undefined`.
|
|
115
|
+
* @public
|
|
116
|
+
*/
|
|
117
|
+
export declare function NumberInput({ value, onChange, label, min, max, step }: INumberInputProps): React.ReactElement;
|
|
118
|
+
/**
|
|
119
|
+
* Props for the SelectInput component.
|
|
120
|
+
* @public
|
|
121
|
+
*/
|
|
122
|
+
export interface ISelectInputProps<T extends string> {
|
|
123
|
+
/** Currently selected value */
|
|
124
|
+
readonly value: T;
|
|
125
|
+
/** Available options */
|
|
126
|
+
readonly options: ReadonlyArray<T>;
|
|
127
|
+
/** Called when selection changes */
|
|
128
|
+
readonly onChange: (value: T) => void;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Dropdown select for enumerated string values.
|
|
132
|
+
* @public
|
|
133
|
+
*/
|
|
134
|
+
export declare function SelectInput<T extends string>({ value, options, onChange }: ISelectInputProps<T>): React.ReactElement;
|
|
135
|
+
/**
|
|
136
|
+
* Props for the TagsInput component.
|
|
137
|
+
* @public
|
|
138
|
+
*/
|
|
139
|
+
export interface ITagsInputProps {
|
|
140
|
+
/** Current tags (undefined means no tags) */
|
|
141
|
+
readonly value: ReadonlyArray<string> | undefined;
|
|
142
|
+
/** Called when tags change (empty → undefined) */
|
|
143
|
+
readonly onChange: (value: ReadonlyArray<string> | undefined) => void;
|
|
144
|
+
/** Placeholder text */
|
|
145
|
+
readonly placeholder?: string;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Comma-separated tag editor for string arrays.
|
|
149
|
+
* Empty input produces `undefined`.
|
|
150
|
+
* @public
|
|
151
|
+
*/
|
|
152
|
+
export declare function TagsInput({ value, onChange, placeholder }: ITagsInputProps): React.ReactElement;
|
|
153
|
+
/**
|
|
154
|
+
* Props for the CheckboxInput component.
|
|
155
|
+
* @public
|
|
156
|
+
*/
|
|
157
|
+
export interface ICheckboxInputProps {
|
|
158
|
+
/** Current value (undefined treated as false) */
|
|
159
|
+
readonly value: boolean | undefined;
|
|
160
|
+
/** Called when value changes (false → undefined) */
|
|
161
|
+
readonly onChange: (value: boolean | undefined) => void;
|
|
162
|
+
/** Label text displayed next to the checkbox */
|
|
163
|
+
readonly label: string;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Checkbox input for optional boolean fields.
|
|
167
|
+
* Unchecked produces `undefined`.
|
|
168
|
+
* @public
|
|
169
|
+
*/
|
|
170
|
+
export declare function CheckboxInput({ value, onChange, label }: ICheckboxInputProps): React.ReactElement;
|
|
171
|
+
//# sourceMappingURL=EditFieldHelpers.d.ts.map
|
|
@@ -0,0 +1,144 @@
|
|
|
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 __importDefault = (this && this.__importDefault) || function (mod) {
|
|
24
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
25
|
+
};
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.EditField = EditField;
|
|
28
|
+
exports.EditSection = EditSection;
|
|
29
|
+
exports.TextInput = TextInput;
|
|
30
|
+
exports.OptionalTextInput = OptionalTextInput;
|
|
31
|
+
exports.TextAreaInput = TextAreaInput;
|
|
32
|
+
exports.NumberInput = NumberInput;
|
|
33
|
+
exports.SelectInput = SelectInput;
|
|
34
|
+
exports.TagsInput = TagsInput;
|
|
35
|
+
exports.CheckboxInput = CheckboxInput;
|
|
36
|
+
/**
|
|
37
|
+
* Generic edit field helper components for entity editors.
|
|
38
|
+
*
|
|
39
|
+
* Provides reusable form field primitives: text inputs, number inputs,
|
|
40
|
+
* select dropdowns, tag editors, checkboxes, and layout wrappers.
|
|
41
|
+
* These components are domain-agnostic and can be used by any entity editor.
|
|
42
|
+
*
|
|
43
|
+
* @packageDocumentation
|
|
44
|
+
*/
|
|
45
|
+
const react_1 = __importDefault(require("react"));
|
|
46
|
+
/**
|
|
47
|
+
* Horizontal label + field layout for a single edit field.
|
|
48
|
+
* @public
|
|
49
|
+
*/
|
|
50
|
+
function EditField({ label, children }) {
|
|
51
|
+
return (react_1.default.createElement("div", { className: "flex items-baseline gap-2 py-1" },
|
|
52
|
+
react_1.default.createElement("label", { className: "text-xs text-muted w-32 shrink-0" }, label),
|
|
53
|
+
react_1.default.createElement("div", { className: "flex-1" }, children)));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Titled section wrapper for grouping related edit fields.
|
|
57
|
+
* @public
|
|
58
|
+
*/
|
|
59
|
+
function EditSection({ title, children }) {
|
|
60
|
+
return (react_1.default.createElement("div", { className: "mb-4" },
|
|
61
|
+
react_1.default.createElement("h4", { className: "text-xs font-medium text-muted uppercase tracking-wider mb-1.5" }, title),
|
|
62
|
+
children));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Single-line text input for required string fields.
|
|
66
|
+
* @public
|
|
67
|
+
*/
|
|
68
|
+
function TextInput({ value, onChange, placeholder }) {
|
|
69
|
+
return (react_1.default.createElement("input", { type: "text", value: value, onChange: (e) => onChange(e.target.value), placeholder: placeholder, className: "w-full px-2 py-1 text-sm border border-border rounded focus:outline-none focus:ring-1 focus:ring-focus-ring focus:border-focus-ring" }));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Single-line text input for optional string fields.
|
|
73
|
+
* Empty input produces `undefined`.
|
|
74
|
+
* @public
|
|
75
|
+
*/
|
|
76
|
+
function OptionalTextInput({ value, onChange, placeholder }) {
|
|
77
|
+
return (react_1.default.createElement("input", { type: "text", value: value !== null && value !== void 0 ? value : '', onChange: (e) => onChange(e.target.value || undefined), placeholder: placeholder, className: "w-full px-2 py-1 text-sm border border-border rounded focus:outline-none focus:ring-1 focus:ring-focus-ring focus:border-focus-ring" }));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Multi-line text input for optional string fields.
|
|
81
|
+
* Empty input produces `undefined`.
|
|
82
|
+
* @public
|
|
83
|
+
*/
|
|
84
|
+
function TextAreaInput({ value, onChange, placeholder }) {
|
|
85
|
+
return (react_1.default.createElement("textarea", { value: value !== null && value !== void 0 ? value : '', onChange: (e) => onChange(e.target.value || undefined), placeholder: placeholder, rows: 3, className: "w-full px-2 py-1 text-sm border border-border rounded focus:outline-none focus:ring-1 focus:ring-focus-ring focus:border-focus-ring resize-y" }));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Numeric input for optional number fields.
|
|
89
|
+
* Empty input produces `undefined`.
|
|
90
|
+
* @public
|
|
91
|
+
*/
|
|
92
|
+
function NumberInput({ value, onChange, label, min, max, step }) {
|
|
93
|
+
return (react_1.default.createElement("input", { type: "number", value: value !== null && value !== void 0 ? value : '', onChange: (e) => {
|
|
94
|
+
const raw = e.target.value;
|
|
95
|
+
if (!raw.trim()) {
|
|
96
|
+
onChange(undefined);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
const num = parseFloat(raw);
|
|
100
|
+
if (!isNaN(num)) {
|
|
101
|
+
onChange(num);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}, min: min, max: max, step: step !== null && step !== void 0 ? step : 0.1, className: "w-24 px-2 py-1 text-sm border border-border rounded focus:outline-none focus:ring-1 focus:ring-focus-ring focus:border-focus-ring text-right", "aria-label": label }));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Dropdown select for enumerated string values.
|
|
108
|
+
* @public
|
|
109
|
+
*/
|
|
110
|
+
function SelectInput({ value, options, onChange }) {
|
|
111
|
+
return (react_1.default.createElement("select", { value: value, onChange: (e) => onChange(e.target.value), className: "px-2 py-1 text-sm border border-border rounded focus:outline-none focus:ring-1 focus:ring-focus-ring focus:border-focus-ring" }, options.map((opt) => (react_1.default.createElement("option", { key: opt, value: opt }, opt)))));
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Comma-separated tag editor for string arrays.
|
|
115
|
+
* Empty input produces `undefined`.
|
|
116
|
+
* @public
|
|
117
|
+
*/
|
|
118
|
+
function TagsInput({ value, onChange, placeholder }) {
|
|
119
|
+
var _a;
|
|
120
|
+
const text = (_a = value === null || value === void 0 ? void 0 : value.join(', ')) !== null && _a !== void 0 ? _a : '';
|
|
121
|
+
return (react_1.default.createElement("input", { type: "text", value: text, onChange: (e) => {
|
|
122
|
+
const raw = e.target.value;
|
|
123
|
+
if (!raw.trim()) {
|
|
124
|
+
onChange(undefined);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
onChange(raw
|
|
128
|
+
.split(',')
|
|
129
|
+
.map((s) => s.trim())
|
|
130
|
+
.filter((s) => s.length > 0));
|
|
131
|
+
}
|
|
132
|
+
}, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : 'comma-separated values', className: "w-full px-2 py-1 text-sm border border-border rounded focus:outline-none focus:ring-1 focus:ring-focus-ring focus:border-focus-ring" }));
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Checkbox input for optional boolean fields.
|
|
136
|
+
* Unchecked produces `undefined`.
|
|
137
|
+
* @public
|
|
138
|
+
*/
|
|
139
|
+
function CheckboxInput({ value, onChange, label }) {
|
|
140
|
+
return (react_1.default.createElement("label", { className: "flex items-center gap-2 text-sm text-secondary cursor-pointer" },
|
|
141
|
+
react_1.default.createElement("input", { type: "checkbox", checked: value !== null && value !== void 0 ? value : false, onChange: (e) => onChange(e.target.checked || undefined), className: "rounded border-border text-brand-primary focus:ring-focus-ring" }),
|
|
142
|
+
label));
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=EditFieldHelpers.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-action button component with dropdown for alternative actions.
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
*/
|
|
5
|
+
import React from 'react';
|
|
6
|
+
/**
|
|
7
|
+
* Action definition for multi-action button.
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
export interface IMultiActionButtonAction {
|
|
11
|
+
/** Unique identifier for the action. */
|
|
12
|
+
readonly id: string;
|
|
13
|
+
/** Display label for the action. */
|
|
14
|
+
readonly label: string;
|
|
15
|
+
/** Optional icon element to display before the label. */
|
|
16
|
+
readonly icon?: React.ReactNode;
|
|
17
|
+
/** Callback when this action is selected. */
|
|
18
|
+
readonly onSelect: () => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Props for MultiActionButton component.
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
export interface IMultiActionButtonProps {
|
|
25
|
+
/** Primary action that appears on the main button. */
|
|
26
|
+
readonly primaryAction: IMultiActionButtonAction;
|
|
27
|
+
/** Alternative actions shown in the dropdown menu. */
|
|
28
|
+
readonly alternativeActions: readonly IMultiActionButtonAction[];
|
|
29
|
+
/** Optional variant for button styling. */
|
|
30
|
+
readonly variant?: 'primary' | 'default' | 'danger';
|
|
31
|
+
/** If true, the primary action button is disabled. */
|
|
32
|
+
readonly disabled?: boolean;
|
|
33
|
+
/** If true, the dropdown toggle is disabled. Defaults to `disabled`. */
|
|
34
|
+
readonly dropdownDisabled?: boolean;
|
|
35
|
+
/** Optional CSS class name. */
|
|
36
|
+
readonly className?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Multi-action button with dropdown for alternative actions.
|
|
40
|
+
* Similar to Git commit button with "Commit", "Commit & Push", etc.
|
|
41
|
+
*
|
|
42
|
+
* @public
|
|
43
|
+
*/
|
|
44
|
+
export declare function MultiActionButton(props: IMultiActionButtonProps): React.ReactElement;
|
|
45
|
+
//# sourceMappingURL=MultiActionButton.d.ts.map
|
|
@@ -0,0 +1,109 @@
|
|
|
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.MultiActionButton = MultiActionButton;
|
|
58
|
+
/**
|
|
59
|
+
* Multi-action button component with dropdown for alternative actions.
|
|
60
|
+
* @packageDocumentation
|
|
61
|
+
*/
|
|
62
|
+
const react_1 = __importStar(require("react"));
|
|
63
|
+
const solid_1 = require("@heroicons/react/20/solid");
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// Component
|
|
66
|
+
// ============================================================================
|
|
67
|
+
/**
|
|
68
|
+
* Multi-action button with dropdown for alternative actions.
|
|
69
|
+
* Similar to Git commit button with "Commit", "Commit & Push", etc.
|
|
70
|
+
*
|
|
71
|
+
* @public
|
|
72
|
+
*/
|
|
73
|
+
function MultiActionButton(props) {
|
|
74
|
+
const { primaryAction, alternativeActions, variant = 'primary', disabled = false, dropdownDisabled, className = '' } = props;
|
|
75
|
+
const isDropdownDisabled = dropdownDisabled !== null && dropdownDisabled !== void 0 ? dropdownDisabled : disabled;
|
|
76
|
+
const [isOpen, setIsOpen] = (0, react_1.useState)(false);
|
|
77
|
+
const dropdownRef = (0, react_1.useRef)(null);
|
|
78
|
+
// Close dropdown when clicking outside
|
|
79
|
+
(0, react_1.useEffect)(() => {
|
|
80
|
+
const handleClickOutside = (event) => {
|
|
81
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
82
|
+
setIsOpen(false);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
if (isOpen) {
|
|
86
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
87
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
88
|
+
}
|
|
89
|
+
}, [isOpen]);
|
|
90
|
+
const variantClasses = {
|
|
91
|
+
primary: 'bg-brand-primary hover:bg-brand-primary/90 text-white',
|
|
92
|
+
default: 'bg-surface-raised hover:bg-surface-raised text-secondary',
|
|
93
|
+
danger: 'bg-status-error-btn hover:bg-status-error-btn-hover text-white'
|
|
94
|
+
};
|
|
95
|
+
const baseClasses = 'inline-flex items-center text-xs font-medium rounded transition-colors disabled:opacity-40 disabled:cursor-not-allowed';
|
|
96
|
+
return (react_1.default.createElement("div", { className: `relative inline-flex ${className}`, ref: dropdownRef },
|
|
97
|
+
react_1.default.createElement("button", { type: "button", onClick: primaryAction.onSelect, disabled: disabled, className: `${baseClasses} ${variantClasses[variant]} px-2.5 py-1 ${alternativeActions.length > 0 ? 'rounded-r-none' : 'rounded'}` },
|
|
98
|
+
primaryAction.icon,
|
|
99
|
+
react_1.default.createElement("span", null, primaryAction.label)),
|
|
100
|
+
alternativeActions.length > 0 && (react_1.default.createElement("button", { type: "button", onClick: () => setIsOpen(!isOpen), disabled: isDropdownDisabled, className: `${baseClasses} ${variantClasses[variant]} px-1 py-1 rounded-l-none border-l border-white/20` },
|
|
101
|
+
react_1.default.createElement(solid_1.ChevronDownIcon, { className: "h-3.5 w-3.5" }))),
|
|
102
|
+
isOpen && alternativeActions.length > 0 && (react_1.default.createElement("div", { className: "absolute top-full right-0 mt-1 w-48 bg-surface rounded-md shadow-lg border border-border py-1 z-50" }, alternativeActions.map((action) => (react_1.default.createElement("button", { key: action.id, type: "button", onClick: () => {
|
|
103
|
+
action.onSelect();
|
|
104
|
+
setIsOpen(false);
|
|
105
|
+
}, className: "w-full text-left px-3 py-2 text-xs text-secondary hover:bg-surface-raised flex items-center gap-2" },
|
|
106
|
+
action.icon,
|
|
107
|
+
react_1.default.createElement("span", null, action.label))))))));
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=MultiActionButton.js.map
|