@anvilkit/plugin-asset-manager 0.1.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/LICENSE +21 -0
- package/README.md +159 -0
- package/dist/adapters/data-url.cjs +78 -0
- package/dist/adapters/data-url.d.cts +6 -0
- package/dist/adapters/data-url.d.cts.map +1 -0
- package/dist/adapters/data-url.d.ts +6 -0
- package/dist/adapters/data-url.d.ts.map +1 -0
- package/dist/adapters/data-url.js +44 -0
- package/dist/adapters/extract-image-dimensions.cjs +69 -0
- package/dist/adapters/extract-image-dimensions.d.cts +21 -0
- package/dist/adapters/extract-image-dimensions.d.cts.map +1 -0
- package/dist/adapters/extract-image-dimensions.d.ts +21 -0
- package/dist/adapters/extract-image-dimensions.d.ts.map +1 -0
- package/dist/adapters/extract-image-dimensions.js +35 -0
- package/dist/adapters/in-memory.cjs +62 -0
- package/dist/adapters/in-memory.d.cts +3 -0
- package/dist/adapters/in-memory.d.cts.map +1 -0
- package/dist/adapters/in-memory.d.ts +3 -0
- package/dist/adapters/in-memory.d.ts.map +1 -0
- package/dist/adapters/in-memory.js +28 -0
- package/dist/adapters/s3-presigned.cjs +166 -0
- package/dist/adapters/s3-presigned.d.cts +59 -0
- package/dist/adapters/s3-presigned.d.cts.map +1 -0
- package/dist/adapters/s3-presigned.d.ts +59 -0
- package/dist/adapters/s3-presigned.d.ts.map +1 -0
- package/dist/adapters/s3-presigned.js +129 -0
- package/dist/asset-reference.cjs +38 -0
- package/dist/asset-reference.d.cts +10 -0
- package/dist/asset-reference.d.cts.map +1 -0
- package/dist/asset-reference.d.ts +10 -0
- package/dist/asset-reference.d.ts.map +1 -0
- package/dist/asset-reference.js +4 -0
- package/dist/csp.cjs +83 -0
- package/dist/csp.d.cts +45 -0
- package/dist/csp.d.cts.map +1 -0
- package/dist/csp.d.ts +45 -0
- package/dist/csp.d.ts.map +1 -0
- package/dist/csp.js +49 -0
- package/dist/errors.cjs +58 -0
- package/dist/errors.d.cts +15 -0
- package/dist/errors.d.cts.map +1 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +21 -0
- package/dist/header-action.cjs +44 -0
- package/dist/header-action.d.cts +3 -0
- package/dist/header-action.d.cts.map +1 -0
- package/dist/header-action.d.ts +3 -0
- package/dist/header-action.d.ts.map +1 -0
- package/dist/header-action.js +10 -0
- package/dist/index.cjs +90 -0
- package/dist/index.d.cts +18 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/infer-kind.cjs +45 -0
- package/dist/infer-kind.d.cts +8 -0
- package/dist/infer-kind.d.cts.map +1 -0
- package/dist/infer-kind.d.ts +8 -0
- package/dist/infer-kind.d.ts.map +1 -0
- package/dist/infer-kind.js +11 -0
- package/dist/plugin.cjs +258 -0
- package/dist/plugin.d.cts +9 -0
- package/dist/plugin.d.cts.map +1 -0
- package/dist/plugin.d.ts +9 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +212 -0
- package/dist/registry.cjs +198 -0
- package/dist/registry.d.cts +3 -0
- package/dist/registry.d.cts.map +1 -0
- package/dist/registry.d.ts +3 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +164 -0
- package/dist/resolver.cjs +185 -0
- package/dist/resolver.d.cts +10 -0
- package/dist/resolver.d.cts.map +1 -0
- package/dist/resolver.d.ts +10 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +148 -0
- package/dist/retry.cjs +123 -0
- package/dist/retry.d.cts +71 -0
- package/dist/retry.d.cts.map +1 -0
- package/dist/retry.d.ts +71 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +86 -0
- package/dist/studio-asset-source.cjs +211 -0
- package/dist/studio-asset-source.d.cts +52 -0
- package/dist/studio-asset-source.d.cts.map +1 -0
- package/dist/studio-asset-source.d.ts +52 -0
- package/dist/studio-asset-source.d.ts.map +1 -0
- package/dist/studio-asset-source.js +171 -0
- package/dist/testing/index.cjs +66 -0
- package/dist/testing/index.d.cts +24 -0
- package/dist/testing/index.d.cts.map +1 -0
- package/dist/testing/index.d.ts +24 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +29 -0
- package/dist/types.cjs +18 -0
- package/dist/types.d.cts +132 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.ts +132 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +0 -0
- package/dist/ui/AssetBrowser.cjs +271 -0
- package/dist/ui/AssetBrowser.d.cts +45 -0
- package/dist/ui/AssetBrowser.d.cts.map +1 -0
- package/dist/ui/AssetBrowser.d.ts +45 -0
- package/dist/ui/AssetBrowser.d.ts.map +1 -0
- package/dist/ui/AssetBrowser.js +237 -0
- package/dist/ui/AssetCommandPalette.cjs +135 -0
- package/dist/ui/AssetCommandPalette.d.cts +21 -0
- package/dist/ui/AssetCommandPalette.d.cts.map +1 -0
- package/dist/ui/AssetCommandPalette.d.ts +21 -0
- package/dist/ui/AssetCommandPalette.d.ts.map +1 -0
- package/dist/ui/AssetCommandPalette.js +101 -0
- package/dist/ui/AssetManagerUI.cjs +169 -0
- package/dist/ui/AssetManagerUI.d.cts +15 -0
- package/dist/ui/AssetManagerUI.d.cts.map +1 -0
- package/dist/ui/AssetManagerUI.d.ts +15 -0
- package/dist/ui/AssetManagerUI.d.ts.map +1 -0
- package/dist/ui/AssetManagerUI.js +135 -0
- package/dist/ui/DeleteAssetDialog.cjs +70 -0
- package/dist/ui/DeleteAssetDialog.d.cts +22 -0
- package/dist/ui/DeleteAssetDialog.d.cts.map +1 -0
- package/dist/ui/DeleteAssetDialog.d.ts +22 -0
- package/dist/ui/DeleteAssetDialog.d.ts.map +1 -0
- package/dist/ui/DeleteAssetDialog.js +36 -0
- package/dist/ui/MetadataPanel.cjs +147 -0
- package/dist/ui/MetadataPanel.d.cts +21 -0
- package/dist/ui/MetadataPanel.d.cts.map +1 -0
- package/dist/ui/MetadataPanel.d.ts +21 -0
- package/dist/ui/MetadataPanel.d.ts.map +1 -0
- package/dist/ui/MetadataPanel.js +113 -0
- package/dist/ui/ReplaceAssetDialog.cjs +125 -0
- package/dist/ui/ReplaceAssetDialog.d.cts +14 -0
- package/dist/ui/ReplaceAssetDialog.d.cts.map +1 -0
- package/dist/ui/ReplaceAssetDialog.d.ts +14 -0
- package/dist/ui/ReplaceAssetDialog.d.ts.map +1 -0
- package/dist/ui/ReplaceAssetDialog.js +91 -0
- package/dist/ui/UploadButton.cjs +189 -0
- package/dist/ui/UploadButton.d.cts +17 -0
- package/dist/ui/UploadButton.d.cts.map +1 -0
- package/dist/ui/UploadButton.d.ts +17 -0
- package/dist/ui/UploadButton.d.ts.map +1 -0
- package/dist/ui/UploadButton.js +155 -0
- package/dist/ui/index.cjs +60 -0
- package/dist/ui/index.d.cts +15 -0
- package/dist/ui/index.d.cts.map +1 -0
- package/dist/ui/index.d.ts +15 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +7 -0
- package/dist/validate-upload-result.cjs +149 -0
- package/dist/validate-upload-result.d.cts +9 -0
- package/dist/validate-upload-result.d.cts.map +1 -0
- package/dist/validate-upload-result.d.ts +9 -0
- package/dist/validate-upload-result.d.ts.map +1 -0
- package/dist/validate-upload-result.js +115 -0
- package/package.json +131 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@anvilkit/ui/card";
|
|
2
|
+
import { Input } from "@anvilkit/ui/input";
|
|
3
|
+
import { inferAssetKind } from "../infer-kind.js";
|
|
4
|
+
import * as __rspack_external_react from "react";
|
|
5
|
+
const KIND_FILTERS = [
|
|
6
|
+
"image",
|
|
7
|
+
"video",
|
|
8
|
+
"audio",
|
|
9
|
+
"font",
|
|
10
|
+
"document"
|
|
11
|
+
];
|
|
12
|
+
const DEFAULT_VIRTUALIZE_THRESHOLD = 50;
|
|
13
|
+
const DEFAULT_ITEM_HEIGHT = 56;
|
|
14
|
+
const DEFAULT_MAX_HEIGHT = 400;
|
|
15
|
+
const DEFAULT_PAGE_SIZE = 100;
|
|
16
|
+
const OVERSCAN = 4;
|
|
17
|
+
function AssetBrowser({ assets, onInsert, onDelete, onReplace, onEdit, searchEnabled = false, pageSize = DEFAULT_PAGE_SIZE, virtualizeThreshold = DEFAULT_VIRTUALIZE_THRESHOLD, itemHeight = DEFAULT_ITEM_HEIGHT, maxHeight = DEFAULT_MAX_HEIGHT }) {
|
|
18
|
+
const [activeIndex, setActiveIndex] = __rspack_external_react.useState(assets.length > 0 ? 0 : -1);
|
|
19
|
+
const [scrollTop, setScrollTop] = __rspack_external_react.useState(0);
|
|
20
|
+
const [query, setQuery] = __rspack_external_react.useState("");
|
|
21
|
+
const [activeKinds, setActiveKinds] = __rspack_external_react.useState([]);
|
|
22
|
+
const [pageLimit, setPageLimit] = __rspack_external_react.useState(pageSize);
|
|
23
|
+
const buttonRefs = __rspack_external_react.useRef([]);
|
|
24
|
+
const scrollContainerRef = __rspack_external_react.useRef(null);
|
|
25
|
+
const filteredAssets = __rspack_external_react.useMemo(()=>{
|
|
26
|
+
if (!searchEnabled) return assets;
|
|
27
|
+
const lower = query.trim().toLowerCase();
|
|
28
|
+
return assets.filter((asset)=>{
|
|
29
|
+
if (activeKinds.length > 0) {
|
|
30
|
+
if (!activeKinds.includes(inferAssetKind(asset))) return false;
|
|
31
|
+
}
|
|
32
|
+
if ("" === lower) return true;
|
|
33
|
+
if (asset.id.toLowerCase().includes(lower)) return true;
|
|
34
|
+
if (asset.name?.toLowerCase().includes(lower)) return true;
|
|
35
|
+
if (asset.meta?.mimeType?.toLowerCase().includes(lower)) return true;
|
|
36
|
+
if (asset.tags?.some((tag)=>tag.toLowerCase().includes(lower))) return true;
|
|
37
|
+
return false;
|
|
38
|
+
});
|
|
39
|
+
}, [
|
|
40
|
+
assets,
|
|
41
|
+
activeKinds,
|
|
42
|
+
query,
|
|
43
|
+
searchEnabled
|
|
44
|
+
]);
|
|
45
|
+
const visibleSlice = __rspack_external_react.useMemo(()=>searchEnabled ? filteredAssets.slice(0, pageLimit) : filteredAssets, [
|
|
46
|
+
filteredAssets,
|
|
47
|
+
pageLimit,
|
|
48
|
+
searchEnabled
|
|
49
|
+
]);
|
|
50
|
+
const total = visibleSlice.length;
|
|
51
|
+
const isVirtualized = total > virtualizeThreshold;
|
|
52
|
+
const hasMore = searchEnabled && filteredAssets.length > visibleSlice.length;
|
|
53
|
+
__rspack_external_react.useEffect(()=>{
|
|
54
|
+
if (0 === total) return void setActiveIndex(-1);
|
|
55
|
+
setActiveIndex((currentIndex)=>currentIndex >= 0 && currentIndex < total ? currentIndex : 0);
|
|
56
|
+
}, [
|
|
57
|
+
total
|
|
58
|
+
]);
|
|
59
|
+
function moveFocus(nextIndex) {
|
|
60
|
+
if (0 === total) return;
|
|
61
|
+
const clampedIndex = Math.max(0, Math.min(nextIndex, total - 1));
|
|
62
|
+
setActiveIndex(clampedIndex);
|
|
63
|
+
if (isVirtualized && scrollContainerRef.current) {
|
|
64
|
+
const targetTop = clampedIndex * itemHeight;
|
|
65
|
+
const targetBottom = targetTop + itemHeight;
|
|
66
|
+
const viewTop = scrollContainerRef.current.scrollTop;
|
|
67
|
+
const viewBottom = viewTop + maxHeight;
|
|
68
|
+
if (targetTop < viewTop) scrollContainerRef.current.scrollTop = targetTop;
|
|
69
|
+
else if (targetBottom > viewBottom) scrollContainerRef.current.scrollTop = targetBottom - maxHeight;
|
|
70
|
+
queueMicrotask(()=>{
|
|
71
|
+
buttonRefs.current[clampedIndex]?.focus();
|
|
72
|
+
});
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
buttonRefs.current[clampedIndex]?.focus();
|
|
76
|
+
}
|
|
77
|
+
function toggleKind(kind) {
|
|
78
|
+
setActiveKinds((current)=>current.includes(kind) ? current.filter((entry)=>entry !== kind) : [
|
|
79
|
+
...current,
|
|
80
|
+
kind
|
|
81
|
+
]);
|
|
82
|
+
}
|
|
83
|
+
const firstVisible = isVirtualized ? Math.max(0, Math.floor(scrollTop / itemHeight) - OVERSCAN) : 0;
|
|
84
|
+
const lastVisible = isVirtualized ? Math.min(total - 1, Math.ceil((scrollTop + maxHeight) / itemHeight) + OVERSCAN) : total - 1;
|
|
85
|
+
const visibleAssets = 0 === total ? [] : isVirtualized ? visibleSlice.slice(firstVisible, lastVisible + 1) : visibleSlice;
|
|
86
|
+
function renderRow(asset, index) {
|
|
87
|
+
return /*#__PURE__*/ __rspack_external_react.createElement("li", {
|
|
88
|
+
"aria-posinset": index + 1,
|
|
89
|
+
"aria-setsize": total,
|
|
90
|
+
key: asset.id,
|
|
91
|
+
role: "listitem"
|
|
92
|
+
}, /*#__PURE__*/ __rspack_external_react.createElement("button", {
|
|
93
|
+
"aria-label": `Insert asset ${asset.id}`,
|
|
94
|
+
onClick: ()=>{
|
|
95
|
+
onInsert(asset);
|
|
96
|
+
},
|
|
97
|
+
onFocus: ()=>{
|
|
98
|
+
setActiveIndex(index);
|
|
99
|
+
},
|
|
100
|
+
onKeyDown: (event)=>{
|
|
101
|
+
if ("ArrowDown" === event.key) {
|
|
102
|
+
event.preventDefault();
|
|
103
|
+
moveFocus(index + 1);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if ("ArrowUp" === event.key) {
|
|
107
|
+
event.preventDefault();
|
|
108
|
+
moveFocus(index - 1);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if ("Home" === event.key) {
|
|
112
|
+
event.preventDefault();
|
|
113
|
+
moveFocus(0);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if ("End" === event.key) {
|
|
117
|
+
event.preventDefault();
|
|
118
|
+
moveFocus(total - 1);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if ("Enter" === event.key || " " === event.key) {
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
onInsert(asset);
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
ref: (node)=>{
|
|
127
|
+
buttonRefs.current[index] = node;
|
|
128
|
+
},
|
|
129
|
+
tabIndex: activeIndex === index ? 0 : -1,
|
|
130
|
+
type: "button"
|
|
131
|
+
}, /*#__PURE__*/ __rspack_external_react.createElement("span", null, asset.id), /*#__PURE__*/ __rspack_external_react.createElement("span", null, asset.meta?.mimeType ?? "unknown type")), void 0 !== onEdit ? /*#__PURE__*/ __rspack_external_react.createElement("button", {
|
|
132
|
+
"aria-label": `Edit asset ${asset.id}`,
|
|
133
|
+
"data-asset-action": "edit",
|
|
134
|
+
onClick: ()=>{
|
|
135
|
+
onEdit(asset);
|
|
136
|
+
},
|
|
137
|
+
type: "button"
|
|
138
|
+
}, "Edit") : null, void 0 !== onReplace ? /*#__PURE__*/ __rspack_external_react.createElement("button", {
|
|
139
|
+
"aria-label": `Replace asset ${asset.id}`,
|
|
140
|
+
"data-asset-action": "replace",
|
|
141
|
+
onClick: ()=>{
|
|
142
|
+
onReplace(asset);
|
|
143
|
+
},
|
|
144
|
+
type: "button"
|
|
145
|
+
}, "Replace") : null, void 0 !== onDelete ? /*#__PURE__*/ __rspack_external_react.createElement("button", {
|
|
146
|
+
"aria-label": `Delete asset ${asset.id}`,
|
|
147
|
+
"data-asset-action": "delete",
|
|
148
|
+
onClick: ()=>{
|
|
149
|
+
onDelete(asset);
|
|
150
|
+
},
|
|
151
|
+
type: "button"
|
|
152
|
+
}, "Delete") : null);
|
|
153
|
+
}
|
|
154
|
+
const filterRow = searchEnabled ? /*#__PURE__*/ __rspack_external_react.createElement("div", {
|
|
155
|
+
"data-asset-manager-filters": true
|
|
156
|
+
}, /*#__PURE__*/ __rspack_external_react.createElement(Input, {
|
|
157
|
+
"aria-label": "Search assets",
|
|
158
|
+
onChange: (event)=>{
|
|
159
|
+
setQuery(event.target.value);
|
|
160
|
+
setPageLimit(pageSize);
|
|
161
|
+
},
|
|
162
|
+
placeholder: "Search by name, tag, or MIME",
|
|
163
|
+
value: query
|
|
164
|
+
}), /*#__PURE__*/ __rspack_external_react.createElement("div", {
|
|
165
|
+
"aria-label": "Asset kind filters",
|
|
166
|
+
role: "group"
|
|
167
|
+
}, KIND_FILTERS.map((kind)=>{
|
|
168
|
+
const active = activeKinds.includes(kind);
|
|
169
|
+
return /*#__PURE__*/ __rspack_external_react.createElement("button", {
|
|
170
|
+
"aria-label": `Filter ${kind} assets`,
|
|
171
|
+
"aria-pressed": active,
|
|
172
|
+
"data-asset-kind-filter": kind,
|
|
173
|
+
key: kind,
|
|
174
|
+
onClick: ()=>{
|
|
175
|
+
toggleKind(kind);
|
|
176
|
+
setPageLimit(pageSize);
|
|
177
|
+
},
|
|
178
|
+
type: "button"
|
|
179
|
+
}, kind);
|
|
180
|
+
}))) : null;
|
|
181
|
+
if (0 === total) {
|
|
182
|
+
const emptyLabel = searchEnabled && ("" !== query || activeKinds.length > 0) ? "No assets match the current filters." : "No assets uploaded yet.";
|
|
183
|
+
return /*#__PURE__*/ __rspack_external_react.createElement(Card, null, /*#__PURE__*/ __rspack_external_react.createElement(CardHeader, null, /*#__PURE__*/ __rspack_external_react.createElement(CardTitle, null, "Asset browser"), /*#__PURE__*/ __rspack_external_react.createElement(CardDescription, null, "Validated assets currently registered in memory.")), /*#__PURE__*/ __rspack_external_react.createElement(CardContent, null, filterRow, /*#__PURE__*/ __rspack_external_react.createElement("ul", {
|
|
184
|
+
"aria-label": "Assets",
|
|
185
|
+
role: "list"
|
|
186
|
+
}, /*#__PURE__*/ __rspack_external_react.createElement("li", {
|
|
187
|
+
role: "listitem"
|
|
188
|
+
}, emptyLabel))));
|
|
189
|
+
}
|
|
190
|
+
if (!isVirtualized) return /*#__PURE__*/ __rspack_external_react.createElement(Card, null, /*#__PURE__*/ __rspack_external_react.createElement(CardHeader, null, /*#__PURE__*/ __rspack_external_react.createElement(CardTitle, null, "Asset browser"), /*#__PURE__*/ __rspack_external_react.createElement(CardDescription, null, "Validated assets currently registered in memory.")), /*#__PURE__*/ __rspack_external_react.createElement(CardContent, null, filterRow, /*#__PURE__*/ __rspack_external_react.createElement("ul", {
|
|
191
|
+
"aria-label": "Assets",
|
|
192
|
+
role: "list"
|
|
193
|
+
}, visibleAssets.map((asset, offset)=>renderRow(asset, offset))), hasMore ? /*#__PURE__*/ __rspack_external_react.createElement("button", {
|
|
194
|
+
"data-asset-action": "load-more",
|
|
195
|
+
onClick: ()=>{
|
|
196
|
+
setPageLimit((current)=>current + pageSize);
|
|
197
|
+
},
|
|
198
|
+
type: "button"
|
|
199
|
+
}, "Load more") : null));
|
|
200
|
+
const totalHeight = total * itemHeight;
|
|
201
|
+
const offsetY = firstVisible * itemHeight;
|
|
202
|
+
return /*#__PURE__*/ __rspack_external_react.createElement(Card, null, /*#__PURE__*/ __rspack_external_react.createElement(CardHeader, null, /*#__PURE__*/ __rspack_external_react.createElement(CardTitle, null, "Asset browser"), /*#__PURE__*/ __rspack_external_react.createElement(CardDescription, null, "Validated assets currently registered in memory.")), /*#__PURE__*/ __rspack_external_react.createElement(CardContent, null, filterRow, /*#__PURE__*/ __rspack_external_react.createElement("div", {
|
|
203
|
+
"data-asset-manager-virtual": true,
|
|
204
|
+
onScroll: (event)=>{
|
|
205
|
+
setScrollTop(event.currentTarget.scrollTop);
|
|
206
|
+
},
|
|
207
|
+
ref: scrollContainerRef,
|
|
208
|
+
style: {
|
|
209
|
+
height: maxHeight,
|
|
210
|
+
overflowY: "auto",
|
|
211
|
+
position: "relative"
|
|
212
|
+
}
|
|
213
|
+
}, /*#__PURE__*/ __rspack_external_react.createElement("div", {
|
|
214
|
+
style: {
|
|
215
|
+
height: totalHeight,
|
|
216
|
+
position: "relative"
|
|
217
|
+
}
|
|
218
|
+
}, /*#__PURE__*/ __rspack_external_react.createElement("ul", {
|
|
219
|
+
"aria-label": "Assets",
|
|
220
|
+
role: "list",
|
|
221
|
+
style: {
|
|
222
|
+
margin: 0,
|
|
223
|
+
padding: 0,
|
|
224
|
+
position: "absolute",
|
|
225
|
+
top: offsetY,
|
|
226
|
+
left: 0,
|
|
227
|
+
right: 0
|
|
228
|
+
}
|
|
229
|
+
}, visibleAssets.map((asset, offset)=>renderRow(asset, firstVisible + offset))))), hasMore ? /*#__PURE__*/ __rspack_external_react.createElement("button", {
|
|
230
|
+
"data-asset-action": "load-more",
|
|
231
|
+
onClick: ()=>{
|
|
232
|
+
setPageLimit((current)=>current + pageSize);
|
|
233
|
+
},
|
|
234
|
+
type: "button"
|
|
235
|
+
}, "Load more") : null));
|
|
236
|
+
}
|
|
237
|
+
export { AssetBrowser };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
AssetCommandPalette: ()=>AssetCommandPalette
|
|
28
|
+
});
|
|
29
|
+
const dialog_namespaceObject = require("@anvilkit/ui/dialog");
|
|
30
|
+
const input_namespaceObject = require("@anvilkit/ui/input");
|
|
31
|
+
const external_react_namespaceObject = require("react");
|
|
32
|
+
const external_infer_kind_cjs_namespaceObject = require("../infer-kind.cjs");
|
|
33
|
+
const DEFAULT_MAX_RESULTS = 20;
|
|
34
|
+
function AssetCommandPalette({ registry, open, onOpenChange, onSelect, maxResults = DEFAULT_MAX_RESULTS }) {
|
|
35
|
+
const [query, setQuery] = external_react_namespaceObject.useState("");
|
|
36
|
+
const [results, setResults] = external_react_namespaceObject.useState([]);
|
|
37
|
+
const [activeIndex, setActiveIndex] = external_react_namespaceObject.useState(0);
|
|
38
|
+
const inputRef = external_react_namespaceObject.useRef(null);
|
|
39
|
+
const listRef = external_react_namespaceObject.useRef(null);
|
|
40
|
+
const refresh = external_react_namespaceObject.useCallback((nextQuery)=>{
|
|
41
|
+
const page = registry.search({
|
|
42
|
+
query: nextQuery,
|
|
43
|
+
limit: maxResults
|
|
44
|
+
});
|
|
45
|
+
setResults(page.items);
|
|
46
|
+
setActiveIndex(0);
|
|
47
|
+
}, [
|
|
48
|
+
registry,
|
|
49
|
+
maxResults
|
|
50
|
+
]);
|
|
51
|
+
external_react_namespaceObject.useEffect(()=>{
|
|
52
|
+
if (!open) return;
|
|
53
|
+
refresh(query);
|
|
54
|
+
const unsubscribe = registry.subscribe(()=>{
|
|
55
|
+
refresh(query);
|
|
56
|
+
});
|
|
57
|
+
return unsubscribe;
|
|
58
|
+
}, [
|
|
59
|
+
open,
|
|
60
|
+
query,
|
|
61
|
+
registry,
|
|
62
|
+
refresh
|
|
63
|
+
]);
|
|
64
|
+
external_react_namespaceObject.useEffect(()=>{
|
|
65
|
+
if (!open) return;
|
|
66
|
+
setQuery("");
|
|
67
|
+
queueMicrotask(()=>{
|
|
68
|
+
inputRef.current?.focus();
|
|
69
|
+
});
|
|
70
|
+
}, [
|
|
71
|
+
open
|
|
72
|
+
]);
|
|
73
|
+
function handleKeyDown(event) {
|
|
74
|
+
if ("ArrowDown" === event.key) {
|
|
75
|
+
event.preventDefault();
|
|
76
|
+
setActiveIndex((current)=>0 === results.length ? 0 : Math.min(current + 1, results.length - 1));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if ("ArrowUp" === event.key) {
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
setActiveIndex((current)=>Math.max(current - 1, 0));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if ("Enter" === event.key) {
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
const picked = results[activeIndex];
|
|
87
|
+
if (void 0 !== picked) {
|
|
88
|
+
onSelect(picked);
|
|
89
|
+
onOpenChange(false);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return /*#__PURE__*/ external_react_namespaceObject.createElement(dialog_namespaceObject.Dialog, {
|
|
94
|
+
open: open,
|
|
95
|
+
onOpenChange: onOpenChange
|
|
96
|
+
}, /*#__PURE__*/ external_react_namespaceObject.createElement(dialog_namespaceObject.DialogContent, null, /*#__PURE__*/ external_react_namespaceObject.createElement(dialog_namespaceObject.DialogHeader, null, /*#__PURE__*/ external_react_namespaceObject.createElement(dialog_namespaceObject.DialogTitle, null, "Find an asset"), /*#__PURE__*/ external_react_namespaceObject.createElement(dialog_namespaceObject.DialogDescription, null, "Search by name, id, MIME type, or tag.")), /*#__PURE__*/ external_react_namespaceObject.createElement(input_namespaceObject.Input, {
|
|
97
|
+
"aria-label": "Asset search query",
|
|
98
|
+
onChange: (event)=>{
|
|
99
|
+
setQuery(event.target.value);
|
|
100
|
+
},
|
|
101
|
+
onKeyDown: handleKeyDown,
|
|
102
|
+
placeholder: "Type to search…",
|
|
103
|
+
ref: inputRef,
|
|
104
|
+
value: query
|
|
105
|
+
}), /*#__PURE__*/ external_react_namespaceObject.createElement("ul", {
|
|
106
|
+
"aria-label": "Asset results",
|
|
107
|
+
"data-asset-manager-palette-results": true,
|
|
108
|
+
ref: listRef,
|
|
109
|
+
role: "listbox"
|
|
110
|
+
}, 0 === results.length ? /*#__PURE__*/ external_react_namespaceObject.createElement("li", {
|
|
111
|
+
role: "presentation"
|
|
112
|
+
}, "No matches.") : results.map((asset, index)=>/*#__PURE__*/ external_react_namespaceObject.createElement("li", {
|
|
113
|
+
"aria-selected": index === activeIndex,
|
|
114
|
+
key: asset.id,
|
|
115
|
+
role: "option"
|
|
116
|
+
}, /*#__PURE__*/ external_react_namespaceObject.createElement("button", {
|
|
117
|
+
"aria-label": `Insert asset ${asset.id}`,
|
|
118
|
+
"data-active": index === activeIndex ? "true" : void 0,
|
|
119
|
+
onClick: ()=>{
|
|
120
|
+
onSelect(asset);
|
|
121
|
+
onOpenChange(false);
|
|
122
|
+
},
|
|
123
|
+
onMouseEnter: ()=>{
|
|
124
|
+
setActiveIndex(index);
|
|
125
|
+
},
|
|
126
|
+
type: "button"
|
|
127
|
+
}, /*#__PURE__*/ external_react_namespaceObject.createElement("span", null, asset.name ?? asset.id), /*#__PURE__*/ external_react_namespaceObject.createElement("span", null, asset.meta?.mimeType ?? (0, external_infer_kind_cjs_namespaceObject.inferAssetKind)(asset))))))));
|
|
128
|
+
}
|
|
129
|
+
exports.AssetCommandPalette = __webpack_exports__.AssetCommandPalette;
|
|
130
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
131
|
+
"AssetCommandPalette"
|
|
132
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
133
|
+
Object.defineProperty(exports, '__esModule', {
|
|
134
|
+
value: true
|
|
135
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AssetRegistry, UploadResult } from "../types.js";
|
|
2
|
+
export interface AssetCommandPaletteProps {
|
|
3
|
+
/** Registry the palette searches against. */
|
|
4
|
+
readonly registry: AssetRegistry;
|
|
5
|
+
/** When `true`, the palette is shown. The host owns this state. */
|
|
6
|
+
readonly open: boolean;
|
|
7
|
+
/** Called when the palette is dismissed (Esc, click outside, after pick). */
|
|
8
|
+
readonly onOpenChange: (open: boolean) => void;
|
|
9
|
+
/**
|
|
10
|
+
* Invoked when a result is selected. The host typically calls
|
|
11
|
+
* `onAssetInserted` and closes the palette.
|
|
12
|
+
*/
|
|
13
|
+
readonly onSelect: (asset: UploadResult) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Maximum number of results rendered. Defaults to 20 — enough to
|
|
16
|
+
* surface relevant matches without ballooning the dialog height.
|
|
17
|
+
*/
|
|
18
|
+
readonly maxResults?: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function AssetCommandPalette({ registry, open, onOpenChange, onSelect, maxResults, }: AssetCommandPaletteProps): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
//# sourceMappingURL=AssetCommandPalette.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssetCommandPalette.d.cts","sourceRoot":"","sources":["../../src/ui/AssetCommandPalette.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE/D,MAAM,WAAW,wBAAwB;IACxC,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IACjD;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAID,wBAAgB,mBAAmB,CAAC,EACnC,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,UAAgC,GAChC,EAAE,wBAAwB,2CAmH1B"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AssetRegistry, UploadResult } from "../types.js";
|
|
2
|
+
export interface AssetCommandPaletteProps {
|
|
3
|
+
/** Registry the palette searches against. */
|
|
4
|
+
readonly registry: AssetRegistry;
|
|
5
|
+
/** When `true`, the palette is shown. The host owns this state. */
|
|
6
|
+
readonly open: boolean;
|
|
7
|
+
/** Called when the palette is dismissed (Esc, click outside, after pick). */
|
|
8
|
+
readonly onOpenChange: (open: boolean) => void;
|
|
9
|
+
/**
|
|
10
|
+
* Invoked when a result is selected. The host typically calls
|
|
11
|
+
* `onAssetInserted` and closes the palette.
|
|
12
|
+
*/
|
|
13
|
+
readonly onSelect: (asset: UploadResult) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Maximum number of results rendered. Defaults to 20 — enough to
|
|
16
|
+
* surface relevant matches without ballooning the dialog height.
|
|
17
|
+
*/
|
|
18
|
+
readonly maxResults?: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function AssetCommandPalette({ registry, open, onOpenChange, onSelect, maxResults, }: AssetCommandPaletteProps): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
//# sourceMappingURL=AssetCommandPalette.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssetCommandPalette.d.ts","sourceRoot":"","sources":["../../src/ui/AssetCommandPalette.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE/D,MAAM,WAAW,wBAAwB;IACxC,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IACjD;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAID,wBAAgB,mBAAmB,CAAC,EACnC,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,UAAgC,GAChC,EAAE,wBAAwB,2CAmH1B"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@anvilkit/ui/dialog";
|
|
2
|
+
import { Input } from "@anvilkit/ui/input";
|
|
3
|
+
import { inferAssetKind } from "../infer-kind.js";
|
|
4
|
+
import * as __rspack_external_react from "react";
|
|
5
|
+
const DEFAULT_MAX_RESULTS = 20;
|
|
6
|
+
function AssetCommandPalette({ registry, open, onOpenChange, onSelect, maxResults = DEFAULT_MAX_RESULTS }) {
|
|
7
|
+
const [query, setQuery] = __rspack_external_react.useState("");
|
|
8
|
+
const [results, setResults] = __rspack_external_react.useState([]);
|
|
9
|
+
const [activeIndex, setActiveIndex] = __rspack_external_react.useState(0);
|
|
10
|
+
const inputRef = __rspack_external_react.useRef(null);
|
|
11
|
+
const listRef = __rspack_external_react.useRef(null);
|
|
12
|
+
const refresh = __rspack_external_react.useCallback((nextQuery)=>{
|
|
13
|
+
const page = registry.search({
|
|
14
|
+
query: nextQuery,
|
|
15
|
+
limit: maxResults
|
|
16
|
+
});
|
|
17
|
+
setResults(page.items);
|
|
18
|
+
setActiveIndex(0);
|
|
19
|
+
}, [
|
|
20
|
+
registry,
|
|
21
|
+
maxResults
|
|
22
|
+
]);
|
|
23
|
+
__rspack_external_react.useEffect(()=>{
|
|
24
|
+
if (!open) return;
|
|
25
|
+
refresh(query);
|
|
26
|
+
const unsubscribe = registry.subscribe(()=>{
|
|
27
|
+
refresh(query);
|
|
28
|
+
});
|
|
29
|
+
return unsubscribe;
|
|
30
|
+
}, [
|
|
31
|
+
open,
|
|
32
|
+
query,
|
|
33
|
+
registry,
|
|
34
|
+
refresh
|
|
35
|
+
]);
|
|
36
|
+
__rspack_external_react.useEffect(()=>{
|
|
37
|
+
if (!open) return;
|
|
38
|
+
setQuery("");
|
|
39
|
+
queueMicrotask(()=>{
|
|
40
|
+
inputRef.current?.focus();
|
|
41
|
+
});
|
|
42
|
+
}, [
|
|
43
|
+
open
|
|
44
|
+
]);
|
|
45
|
+
function handleKeyDown(event) {
|
|
46
|
+
if ("ArrowDown" === event.key) {
|
|
47
|
+
event.preventDefault();
|
|
48
|
+
setActiveIndex((current)=>0 === results.length ? 0 : Math.min(current + 1, results.length - 1));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if ("ArrowUp" === event.key) {
|
|
52
|
+
event.preventDefault();
|
|
53
|
+
setActiveIndex((current)=>Math.max(current - 1, 0));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if ("Enter" === event.key) {
|
|
57
|
+
event.preventDefault();
|
|
58
|
+
const picked = results[activeIndex];
|
|
59
|
+
if (void 0 !== picked) {
|
|
60
|
+
onSelect(picked);
|
|
61
|
+
onOpenChange(false);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return /*#__PURE__*/ __rspack_external_react.createElement(Dialog, {
|
|
66
|
+
open: open,
|
|
67
|
+
onOpenChange: onOpenChange
|
|
68
|
+
}, /*#__PURE__*/ __rspack_external_react.createElement(DialogContent, null, /*#__PURE__*/ __rspack_external_react.createElement(DialogHeader, null, /*#__PURE__*/ __rspack_external_react.createElement(DialogTitle, null, "Find an asset"), /*#__PURE__*/ __rspack_external_react.createElement(DialogDescription, null, "Search by name, id, MIME type, or tag.")), /*#__PURE__*/ __rspack_external_react.createElement(Input, {
|
|
69
|
+
"aria-label": "Asset search query",
|
|
70
|
+
onChange: (event)=>{
|
|
71
|
+
setQuery(event.target.value);
|
|
72
|
+
},
|
|
73
|
+
onKeyDown: handleKeyDown,
|
|
74
|
+
placeholder: "Type to search…",
|
|
75
|
+
ref: inputRef,
|
|
76
|
+
value: query
|
|
77
|
+
}), /*#__PURE__*/ __rspack_external_react.createElement("ul", {
|
|
78
|
+
"aria-label": "Asset results",
|
|
79
|
+
"data-asset-manager-palette-results": true,
|
|
80
|
+
ref: listRef,
|
|
81
|
+
role: "listbox"
|
|
82
|
+
}, 0 === results.length ? /*#__PURE__*/ __rspack_external_react.createElement("li", {
|
|
83
|
+
role: "presentation"
|
|
84
|
+
}, "No matches.") : results.map((asset, index)=>/*#__PURE__*/ __rspack_external_react.createElement("li", {
|
|
85
|
+
"aria-selected": index === activeIndex,
|
|
86
|
+
key: asset.id,
|
|
87
|
+
role: "option"
|
|
88
|
+
}, /*#__PURE__*/ __rspack_external_react.createElement("button", {
|
|
89
|
+
"aria-label": `Insert asset ${asset.id}`,
|
|
90
|
+
"data-active": index === activeIndex ? "true" : void 0,
|
|
91
|
+
onClick: ()=>{
|
|
92
|
+
onSelect(asset);
|
|
93
|
+
onOpenChange(false);
|
|
94
|
+
},
|
|
95
|
+
onMouseEnter: ()=>{
|
|
96
|
+
setActiveIndex(index);
|
|
97
|
+
},
|
|
98
|
+
type: "button"
|
|
99
|
+
}, /*#__PURE__*/ __rspack_external_react.createElement("span", null, asset.name ?? asset.id), /*#__PURE__*/ __rspack_external_react.createElement("span", null, asset.meta?.mimeType ?? inferAssetKind(asset))))))));
|
|
100
|
+
}
|
|
101
|
+
export { AssetCommandPalette };
|