@blastlauncher/runtime 1.0.1 → 3.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/api/action-panel.d.ts +134 -0
- package/dist/api/action-panel.js +36 -0
- package/dist/api/action.d.ts +386 -0
- package/dist/api/action.js +260 -0
- package/dist/api/ai-browser.d.ts +423 -0
- package/dist/api/ai-browser.js +113 -0
- package/dist/api/ai-decide.d.ts +41 -0
- package/dist/api/ai-decide.js +62 -0
- package/dist/api/applications.d.ts +13 -0
- package/dist/api/applications.js +48 -0
- package/dist/api/cache.d.ts +27 -0
- package/dist/api/cache.js +126 -0
- package/dist/api/capability.d.ts +14 -0
- package/dist/api/capability.js +30 -0
- package/dist/api/clipboard.d.ts +57 -0
- package/dist/api/clipboard.js +83 -0
- package/dist/api/detail.d.ts +33 -0
- package/dist/api/detail.js +11 -0
- package/dist/api/diagnostics.d.ts +16 -0
- package/dist/api/diagnostics.js +26 -0
- package/dist/api/dropdown.d.ts +17 -0
- package/dist/api/dropdown.js +48 -0
- package/dist/api/enums.generated.d.ts +750 -0
- package/dist/api/enums.generated.js +719 -0
- package/dist/api/environment.d.ts +44 -0
- package/dist/api/environment.js +61 -0
- package/dist/api/feedback.d.ts +94 -0
- package/dist/api/feedback.js +228 -0
- package/dist/api/form.d.ts +162 -0
- package/dist/api/form.js +345 -0
- package/dist/api/grid.d.ts +293 -0
- package/dist/api/grid.js +36 -0
- package/dist/api/implemented.d.ts +53 -0
- package/dist/api/implemented.js +43 -0
- package/dist/api/index.d.ts +612 -0
- package/dist/api/index.js +82 -0
- package/dist/api/keyboard.d.ts +182 -0
- package/dist/api/keyboard.js +107 -0
- package/dist/api/launch.d.ts +40 -0
- package/dist/api/launch.js +27 -0
- package/dist/api/legacy.d.ts +9 -0
- package/dist/api/legacy.js +25 -0
- package/dist/api/list.d.ts +291 -0
- package/dist/api/list.js +28 -0
- package/dist/api/menu-bar.d.ts +54 -0
- package/dist/api/menu-bar.js +30 -0
- package/dist/api/metadata.d.ts +62 -0
- package/dist/api/metadata.js +33 -0
- package/dist/api/navigation.d.ts +36 -0
- package/dist/api/navigation.js +99 -0
- package/dist/api/oauth.d.ts +84 -0
- package/dist/api/oauth.js +125 -0
- package/dist/api/open.d.ts +18 -0
- package/dist/api/open.js +36 -0
- package/dist/api/preferences.d.ts +43 -0
- package/dist/api/preferences.js +41 -0
- package/dist/api/selection.d.ts +10 -0
- package/dist/api/selection.js +31 -0
- package/dist/api/slot.d.ts +7 -0
- package/dist/api/slot.js +11 -0
- package/dist/api/storage.d.ts +24 -0
- package/dist/api/storage.js +45 -0
- package/dist/api/types.d.ts +46 -0
- package/dist/api/types.js +5 -0
- package/dist/api/unsupported.d.ts +18 -0
- package/dist/api/unsupported.generated.d.ts +1255 -0
- package/dist/api/unsupported.generated.js +56 -0
- package/dist/api/unsupported.js +65 -0
- package/dist/api/window-management.d.ts +97 -0
- package/dist/api/window-management.js +164 -0
- package/dist/api/window.d.ts +16 -0
- package/dist/api/window.js +32 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +339 -0
- package/dist/provider.d.ts +8 -0
- package/dist/provider.js +107 -0
- package/dist/reconciler/index.d.ts +9 -0
- package/dist/reconciler/index.js +186 -0
- package/dist/reconciler/serialize.d.ts +3 -0
- package/dist/reconciler/serialize.js +53 -0
- package/dist/reconciler/types.d.ts +8 -0
- package/dist/reconciler/types.js +1 -0
- package/package.json +32 -23
- package/.eslintrc.js +0 -6
- package/CHANGELOG.md +0 -10
- package/dist/App.d.ts +0 -5
- package/dist/App.js +0 -10
- package/dist/components/CommandList/index.d.ts +0 -1
- package/dist/components/CommandList/index.js +0 -20
- package/dist/components/CommandList/utils.d.ts +0 -1
- package/dist/components/CommandList/utils.js +0 -24
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -11
- package/dist/utils/connectDevtools.d.ts +0 -1
- package/dist/utils/connectDevtools.js +0 -11
- package/dist/utils/run.d.ts +0 -4
- package/dist/utils/run.js +0 -12
- package/dist/utils/window.d.ts +0 -0
- package/dist/utils/window.js +0 -2
- package/src/@types/raycast.d.ts +0 -4
- package/src/@types/react-devtools-core.d.ts +0 -19
- package/src/App.tsx +0 -18
- package/src/components/CommandList/index.tsx +0 -27
- package/src/components/CommandList/utils.ts +0 -26
- package/src/index.ts +0 -13
- package/src/utils/connectDevtools.ts +0 -8
- package/src/utils/run.ts +0 -7
- package/src/utils/window.js +0 -1
- package/tsconfig.json +0 -10
package/dist/api/form.js
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { Children, createContext, createElement, forwardRef, isValidElement, useContext, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, } from "react";
|
|
2
|
+
import { requestCapability } from "./capability.js";
|
|
3
|
+
import { DatePickerType } from "./enums.generated.js";
|
|
4
|
+
import { environment } from "./environment.js";
|
|
5
|
+
import { RootNavigationEntryContext } from "./navigation.js";
|
|
6
|
+
import { slot } from "./slot.js";
|
|
7
|
+
/** `Form.DatePicker.Type`: what a date picker lets the user pick. */
|
|
8
|
+
export { DatePickerType };
|
|
9
|
+
let launchDraft;
|
|
10
|
+
/** Installed before importing the command, alongside its launch props. */
|
|
11
|
+
export function setFormDraftValues(values) {
|
|
12
|
+
launchDraft = values;
|
|
13
|
+
}
|
|
14
|
+
const FormContext = createContext(null);
|
|
15
|
+
// A Date does not round-trip through JSON; everything else in `FormValue`
|
|
16
|
+
// does, so only Dates need a tagged encoding.
|
|
17
|
+
function encodeValue(value) {
|
|
18
|
+
return value instanceof Date ? { $date: value.toISOString() } : value;
|
|
19
|
+
}
|
|
20
|
+
function decodeValue(value) {
|
|
21
|
+
if (value && typeof value === "object" && !Array.isArray(value) && "$date" in value) {
|
|
22
|
+
const date = new Date(value.$date);
|
|
23
|
+
if (Number.isFinite(date.getTime()))
|
|
24
|
+
return date;
|
|
25
|
+
throw new Error("Invalid stored Form date");
|
|
26
|
+
}
|
|
27
|
+
if (value === null ||
|
|
28
|
+
typeof value === "string" ||
|
|
29
|
+
typeof value === "boolean" ||
|
|
30
|
+
(typeof value === "number" && Number.isFinite(value)) ||
|
|
31
|
+
(Array.isArray(value) &&
|
|
32
|
+
(value.every((v) => typeof v === "string") ||
|
|
33
|
+
value.every((v) => typeof v === "number" && Number.isFinite(v)))))
|
|
34
|
+
return value;
|
|
35
|
+
throw new Error("Invalid stored Form value");
|
|
36
|
+
}
|
|
37
|
+
/** Decodes a stored or wire-carried values object back into typed `FormValue`s. */
|
|
38
|
+
export function decodeFormValues(values) {
|
|
39
|
+
const result = {};
|
|
40
|
+
for (const [id, value] of Object.entries(values)) {
|
|
41
|
+
try {
|
|
42
|
+
Object.defineProperty(result, id, { value: decodeValue(value), enumerable: true });
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
/* Ignore stale or malformed draft entries. */
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
function fieldKey(id) {
|
|
51
|
+
return `blast:field:${JSON.stringify([environment.commandName, id])}`;
|
|
52
|
+
}
|
|
53
|
+
function draftKey() {
|
|
54
|
+
return `blast:draft:${environment.commandName}`;
|
|
55
|
+
}
|
|
56
|
+
const pendingWrites = new Set();
|
|
57
|
+
function writeStorage(name, params) {
|
|
58
|
+
const promise = requestCapability(name, params);
|
|
59
|
+
pendingWrites.add(promise);
|
|
60
|
+
void promise.then(() => pendingWrites.delete(promise), () => pendingWrites.delete(promise));
|
|
61
|
+
return promise;
|
|
62
|
+
}
|
|
63
|
+
/** Graceful shutdown must receive the host's write acknowledgements before exiting. */
|
|
64
|
+
export async function flushFormPersistence() {
|
|
65
|
+
await Promise.allSettled(pendingWrites);
|
|
66
|
+
}
|
|
67
|
+
async function saveDraft(fields, id, next) {
|
|
68
|
+
const values = {};
|
|
69
|
+
for (const [fieldId, entry] of fields) {
|
|
70
|
+
if (!entry.secret)
|
|
71
|
+
values[fieldId] = encodeValue(entry.read());
|
|
72
|
+
}
|
|
73
|
+
values[id] = encodeValue(next);
|
|
74
|
+
await writeStorage("storage.set", {
|
|
75
|
+
scope: "blast:internal",
|
|
76
|
+
key: draftKey(),
|
|
77
|
+
value: JSON.stringify(values),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
async function clearDraft() {
|
|
81
|
+
await writeStorage("storage.remove", { scope: "blast:internal", key: draftKey() });
|
|
82
|
+
}
|
|
83
|
+
function Field({ kind, initial, secret, fieldRef, ...props }) {
|
|
84
|
+
const ctx = useContext(FormContext);
|
|
85
|
+
const draft = !secret ? ctx?.draft?.[props.id] : undefined;
|
|
86
|
+
const [local, setLocal] = useState((draft !== undefined ? draft : (props.defaultValue ?? initial)));
|
|
87
|
+
const value = props.value !== undefined ? props.value : local;
|
|
88
|
+
const current = useRef(value);
|
|
89
|
+
current.current = value;
|
|
90
|
+
const edited = useRef(false);
|
|
91
|
+
const key = fieldKey(props.id);
|
|
92
|
+
// Restoring a stored value behaves like an uncontrolled `defaultValue`
|
|
93
|
+
// update that arrives after mount; a concurrent edit always wins.
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
if (secret || draft !== undefined || !props.storeValue || props.value !== undefined)
|
|
96
|
+
return;
|
|
97
|
+
let mounted = true;
|
|
98
|
+
void requestCapability("storage.get", { scope: "blast:internal", key })
|
|
99
|
+
.then((stored) => {
|
|
100
|
+
if (!mounted || edited.current || typeof stored !== "string")
|
|
101
|
+
return;
|
|
102
|
+
const restored = decodeValue(JSON.parse(stored));
|
|
103
|
+
if (kind === "DatePicker"
|
|
104
|
+
? restored !== null && !(restored instanceof Date)
|
|
105
|
+
: Array.isArray(initial)
|
|
106
|
+
? !Array.isArray(restored) || !restored.every((v) => typeof v === "string")
|
|
107
|
+
: typeof restored !== typeof initial)
|
|
108
|
+
return;
|
|
109
|
+
current.current = restored;
|
|
110
|
+
setLocal(restored);
|
|
111
|
+
props.onChange?.(restored);
|
|
112
|
+
})
|
|
113
|
+
.catch((error) => console.error("Form field restore failed", error));
|
|
114
|
+
return () => {
|
|
115
|
+
mounted = false;
|
|
116
|
+
};
|
|
117
|
+
}, [key, secret, draft, props.storeValue, props.value]);
|
|
118
|
+
const callbacks = useRef(props);
|
|
119
|
+
callbacks.current = props;
|
|
120
|
+
useLayoutEffect(() => {
|
|
121
|
+
if (!ctx)
|
|
122
|
+
return;
|
|
123
|
+
const read = () => current.current;
|
|
124
|
+
ctx.fields.set(props.id, {
|
|
125
|
+
read,
|
|
126
|
+
secret: !!secret,
|
|
127
|
+
event: (type) => {
|
|
128
|
+
const callback = type === "focus" ? callbacks.current.onFocus : callbacks.current.onBlur;
|
|
129
|
+
callback?.({ target: { id: props.id, value: current.current }, type });
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
return () => {
|
|
133
|
+
if (ctx.fields.get(props.id)?.read === read)
|
|
134
|
+
ctx.fields.delete(props.id);
|
|
135
|
+
};
|
|
136
|
+
}, [ctx?.fields, props.id, secret]);
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
if (props.autoFocus)
|
|
139
|
+
ctx?.focus(props.id);
|
|
140
|
+
}, [ctx?.focus, props.id, props.autoFocus]);
|
|
141
|
+
function change(next) {
|
|
142
|
+
edited.current = true;
|
|
143
|
+
if (props.value === undefined) {
|
|
144
|
+
current.current = next;
|
|
145
|
+
setLocal(next);
|
|
146
|
+
}
|
|
147
|
+
props.onChange?.(next);
|
|
148
|
+
if (secret)
|
|
149
|
+
return;
|
|
150
|
+
if (props.storeValue) {
|
|
151
|
+
void writeStorage("storage.set", {
|
|
152
|
+
scope: "blast:internal",
|
|
153
|
+
key,
|
|
154
|
+
value: JSON.stringify(encodeValue(next)),
|
|
155
|
+
}).catch((error) => console.error("Form field save failed", error));
|
|
156
|
+
}
|
|
157
|
+
if (ctx?.enableDrafts) {
|
|
158
|
+
void saveDraft(ctx.fields, props.id, next).catch((error) => console.error("Form draft save failed", error));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
useImperativeHandle(fieldRef, () => ({
|
|
162
|
+
focus: () => ctx?.focus(props.id),
|
|
163
|
+
reset: () => change((props.defaultValue ?? initial)),
|
|
164
|
+
}), [ctx, props, initial]);
|
|
165
|
+
const scene = { ...props };
|
|
166
|
+
// Secret fields (PasswordField) keep their value runtime-side only: the scene
|
|
167
|
+
// carries a boolean `hasValue` instead of `value`/`defaultValue`, so published
|
|
168
|
+
// scenes and client logs never contain the secret.
|
|
169
|
+
if (secret) {
|
|
170
|
+
delete scene.value;
|
|
171
|
+
delete scene.defaultValue;
|
|
172
|
+
scene.hasValue = value !== undefined && value !== null && value !== "";
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
scene.value = value;
|
|
176
|
+
}
|
|
177
|
+
scene.focused = ctx?.focusedId === props.id;
|
|
178
|
+
return createElement(`Form.${kind}`, {
|
|
179
|
+
...scene,
|
|
180
|
+
onChange: (input) => {
|
|
181
|
+
// Dates arrive from clients as ISO strings; extension callbacks see Dates.
|
|
182
|
+
const next = (kind === "DatePicker" && typeof input === "string" ? new Date(input) : input);
|
|
183
|
+
change(next);
|
|
184
|
+
},
|
|
185
|
+
onFocus: () => ctx?.focus(props.id),
|
|
186
|
+
onBlur: () => {
|
|
187
|
+
ctx?.blur(props.id);
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
function TextField(props, ref) {
|
|
192
|
+
return createElement(Field, { ...props, kind: "TextField", initial: "", fieldRef: ref });
|
|
193
|
+
}
|
|
194
|
+
function PasswordField(props, ref) {
|
|
195
|
+
return createElement(Field, {
|
|
196
|
+
...props,
|
|
197
|
+
kind: "PasswordField",
|
|
198
|
+
initial: "",
|
|
199
|
+
secret: true,
|
|
200
|
+
fieldRef: ref,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function TextArea(props, ref) {
|
|
204
|
+
return createElement(Field, { ...props, kind: "TextArea", initial: "", fieldRef: ref });
|
|
205
|
+
}
|
|
206
|
+
function Checkbox(props, ref) {
|
|
207
|
+
return createElement(Field, {
|
|
208
|
+
...props,
|
|
209
|
+
kind: "Checkbox",
|
|
210
|
+
initial: false,
|
|
211
|
+
fieldRef: ref,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
const DatePicker = Object.assign(forwardRef(function DatePicker(props, ref) {
|
|
215
|
+
return createElement(Field, {
|
|
216
|
+
...props,
|
|
217
|
+
kind: "DatePicker",
|
|
218
|
+
initial: null,
|
|
219
|
+
fieldRef: ref,
|
|
220
|
+
});
|
|
221
|
+
}), {
|
|
222
|
+
Type: DatePickerType,
|
|
223
|
+
/** Whether the date is a full day: non-null with local time exactly midnight. */
|
|
224
|
+
isFullDay(date) {
|
|
225
|
+
return (!!date &&
|
|
226
|
+
date.getHours() === 0 &&
|
|
227
|
+
date.getMinutes() === 0 &&
|
|
228
|
+
date.getSeconds() === 0 &&
|
|
229
|
+
date.getMilliseconds() === 0);
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
function FilePicker(props, ref) {
|
|
233
|
+
return createElement(Field, {
|
|
234
|
+
allowMultipleSelection: true,
|
|
235
|
+
canChooseDirectories: false,
|
|
236
|
+
canChooseFiles: true,
|
|
237
|
+
showHiddenFiles: false,
|
|
238
|
+
...props,
|
|
239
|
+
kind: "FilePicker",
|
|
240
|
+
initial: [],
|
|
241
|
+
fieldRef: ref,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
const TagPicker = Object.assign(forwardRef(function TagPicker(props, ref) {
|
|
245
|
+
return createElement(Field, {
|
|
246
|
+
...props,
|
|
247
|
+
kind: "TagPicker",
|
|
248
|
+
initial: [],
|
|
249
|
+
fieldRef: ref,
|
|
250
|
+
});
|
|
251
|
+
}), {
|
|
252
|
+
Item: (props) => createElement("Form.TagPicker.Item", props),
|
|
253
|
+
});
|
|
254
|
+
function firstValue(children) {
|
|
255
|
+
for (const child of Children.toArray(children)) {
|
|
256
|
+
if (!isValidElement(child))
|
|
257
|
+
continue;
|
|
258
|
+
const value = child.props.value ?? firstValue(child.props.children);
|
|
259
|
+
if (value !== undefined)
|
|
260
|
+
return value;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const Dropdown = Object.assign(forwardRef(function Dropdown(props, ref) {
|
|
264
|
+
return createElement(Field, {
|
|
265
|
+
...props,
|
|
266
|
+
kind: "Dropdown",
|
|
267
|
+
initial: firstValue(props.children) ?? "",
|
|
268
|
+
fieldRef: ref,
|
|
269
|
+
});
|
|
270
|
+
}), {
|
|
271
|
+
Item: (props) => createElement("Form.Dropdown.Item", props),
|
|
272
|
+
Section: (props) => createElement("Form.Dropdown.Section", props),
|
|
273
|
+
});
|
|
274
|
+
export const Form = Object.assign(function Form({ children, actions, searchBarAccessory, ...props }) {
|
|
275
|
+
const [fields] = useState(() => new Map());
|
|
276
|
+
const [focusedId, setFocusedId] = useState(null);
|
|
277
|
+
const parent = useContext(FormContext);
|
|
278
|
+
const rootEntry = useContext(RootNavigationEntryContext);
|
|
279
|
+
const enableDrafts = rootEntry && !parent && !!props.enableDrafts;
|
|
280
|
+
const [draft] = useState(() => (enableDrafts ? launchDraft : undefined));
|
|
281
|
+
const manager = useMemo(() => {
|
|
282
|
+
let active = null;
|
|
283
|
+
const focus = (id) => {
|
|
284
|
+
if (id === active)
|
|
285
|
+
return;
|
|
286
|
+
if (active !== null)
|
|
287
|
+
fields.get(active)?.event("blur");
|
|
288
|
+
active = id;
|
|
289
|
+
setFocusedId(id);
|
|
290
|
+
if (id !== null)
|
|
291
|
+
fields.get(id)?.event("focus");
|
|
292
|
+
};
|
|
293
|
+
return {
|
|
294
|
+
focus,
|
|
295
|
+
blur: (id) => {
|
|
296
|
+
if (active === id)
|
|
297
|
+
focus(null);
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
}, [fields]);
|
|
301
|
+
const ctx = useMemo(() => ({
|
|
302
|
+
fields,
|
|
303
|
+
enableDrafts,
|
|
304
|
+
focusedId,
|
|
305
|
+
...manager,
|
|
306
|
+
draft,
|
|
307
|
+
}), [fields, enableDrafts, focusedId, manager, draft]);
|
|
308
|
+
return createElement(FormContext.Provider, { value: ctx }, createElement("Form", props, children, slot("actions", actions), slot("searchBarAccessory", searchBarAccessory)));
|
|
309
|
+
}, {
|
|
310
|
+
FilePicker: forwardRef(FilePicker),
|
|
311
|
+
TagPicker,
|
|
312
|
+
PasswordField: forwardRef(PasswordField),
|
|
313
|
+
TextField: forwardRef(TextField),
|
|
314
|
+
TextArea: forwardRef(TextArea),
|
|
315
|
+
Checkbox: forwardRef(Checkbox),
|
|
316
|
+
DatePicker,
|
|
317
|
+
Dropdown,
|
|
318
|
+
LinkAccessory: (props) => createElement("Form.LinkAccessory", props),
|
|
319
|
+
Description: (props) => createElement("Form.Description", props),
|
|
320
|
+
Separator: (props) => createElement("Form.Separator", props),
|
|
321
|
+
});
|
|
322
|
+
export function SubmitForm({ onSubmit, title, ...props }) {
|
|
323
|
+
const ctx = useContext(FormContext);
|
|
324
|
+
const [submissionFailureCount, setSubmissionFailureCount] = useState(0);
|
|
325
|
+
return createElement("Action.SubmitForm", {
|
|
326
|
+
submissionFailureCount,
|
|
327
|
+
...props,
|
|
328
|
+
title: title ?? "Submit Form",
|
|
329
|
+
onAction: () => {
|
|
330
|
+
const values = Object.fromEntries(Array.from(ctx?.fields ?? [], ([id, entry]) => [id, entry.read()]));
|
|
331
|
+
const result = onSubmit?.(values);
|
|
332
|
+
const complete = (success) => {
|
|
333
|
+
if (success === false)
|
|
334
|
+
setSubmissionFailureCount((count) => count + 1);
|
|
335
|
+
if (ctx?.enableDrafts && success !== false) {
|
|
336
|
+
void clearDraft().catch((error) => console.error("Form draft clear failed", error));
|
|
337
|
+
}
|
|
338
|
+
return success;
|
|
339
|
+
};
|
|
340
|
+
return result instanceof Promise ? result.then(complete) : complete(result);
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
// `Action.SubmitForm` and its deprecated alias declare a `displayName`.
|
|
345
|
+
SubmitForm.displayName = "SubmitForm";
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { GridFit, GridInset, GridItemSize } from "./enums.generated.js";
|
|
3
|
+
import type { ColorLike, ImageLike } from "./types.js";
|
|
4
|
+
/** `Grid.ItemSize`: the size of the grid's items; deprecated for `columns`. */
|
|
5
|
+
export type GridItemSize = (typeof GridItemSize)[keyof typeof GridItemSize];
|
|
6
|
+
/** `Grid.Inset`: space between an item's content and its borders. */
|
|
7
|
+
export type GridInset = (typeof GridInset)[keyof typeof GridInset];
|
|
8
|
+
/** `Grid.Fit`: how item content fits its cell. */
|
|
9
|
+
export type GridFit = (typeof GridFit)[keyof typeof GridFit];
|
|
10
|
+
/** Allowed `aspectRatio` values, widened from the `GridAspectRatio` enum. */
|
|
11
|
+
export type GridAspectRatio = "1" | "3/2" | "2/3" | "4/3" | "3/4" | "16/9" | "9/16";
|
|
12
|
+
/** `Grid.Item.Props.content`: an image, a color, or either with a tooltip. */
|
|
13
|
+
export type GridItemContent = ImageLike | {
|
|
14
|
+
color: ColorLike;
|
|
15
|
+
} | {
|
|
16
|
+
value: ImageLike | {
|
|
17
|
+
color: ColorLike;
|
|
18
|
+
};
|
|
19
|
+
tooltip: string;
|
|
20
|
+
};
|
|
21
|
+
/** `Grid.Item.Accessory`: an optional icon with a tooltip, under the item. */
|
|
22
|
+
export interface GridItemAccessory {
|
|
23
|
+
/** An optional image shown underneath the item. */
|
|
24
|
+
icon?: ImageLike | null;
|
|
25
|
+
/** An optional tooltip shown when the accessory is hovered. */
|
|
26
|
+
tooltip?: string | null;
|
|
27
|
+
}
|
|
28
|
+
export interface GridProps {
|
|
29
|
+
/** Grid sections or items. */
|
|
30
|
+
children?: ReactNode;
|
|
31
|
+
/** The main title for that view displayed in the host. */
|
|
32
|
+
navigationTitle?: string;
|
|
33
|
+
/** Indicates whether a loading bar should be shown below the search bar. */
|
|
34
|
+
isLoading?: boolean;
|
|
35
|
+
/** The text that will be displayed in the search bar. */
|
|
36
|
+
searchText?: string;
|
|
37
|
+
/** Placeholder text of the search bar. */
|
|
38
|
+
searchBarPlaceholder?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Toggles client-side filtering of the items through the search bar; the
|
|
41
|
+
* client decides how to honor it (see task 0020, "Out of scope").
|
|
42
|
+
*/
|
|
43
|
+
filtering?: boolean | {
|
|
44
|
+
keepSectionOrder: boolean;
|
|
45
|
+
};
|
|
46
|
+
/** Deprecated; use `filtering` instead. */
|
|
47
|
+
enableFiltering?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Defines whether the `onSearchTextChange` handler fires on every keystroke
|
|
50
|
+
* or with a throttling delay.
|
|
51
|
+
*/
|
|
52
|
+
throttle?: boolean;
|
|
53
|
+
/** Callback triggered when the search bar text changes. */
|
|
54
|
+
onSearchTextChange?: (text: string) => void;
|
|
55
|
+
/** Callback triggered when the item selection in the grid changes. */
|
|
56
|
+
onSelectionChange?: (id: string | null) => void;
|
|
57
|
+
/** Selects the item with the specified id. */
|
|
58
|
+
selectedItemId?: string;
|
|
59
|
+
/** Column count for the grid's sections. */
|
|
60
|
+
columns?: number;
|
|
61
|
+
/** Deprecated; use `columns` instead. */
|
|
62
|
+
itemSize?: GridItemSize;
|
|
63
|
+
/** Aspect ratio for the `Grid.Item` elements. */
|
|
64
|
+
aspectRatio?: GridAspectRatio;
|
|
65
|
+
/** Fit for the `Grid.Item` element content. */
|
|
66
|
+
fit?: GridFit;
|
|
67
|
+
/** Space between a `Grid.Item`'s content and its borders. */
|
|
68
|
+
inset?: GridInset;
|
|
69
|
+
/**
|
|
70
|
+
* Pagination configuration; `onLoadMore` fires when the client loads the
|
|
71
|
+
* next page (Blast records the request like any other handler).
|
|
72
|
+
*/
|
|
73
|
+
pagination?: {
|
|
74
|
+
pageSize: number;
|
|
75
|
+
hasMore: boolean;
|
|
76
|
+
onLoadMore: () => void;
|
|
77
|
+
};
|
|
78
|
+
/** An ActionPanel; rendered as a `Slot` child named `actions`. */
|
|
79
|
+
actions?: ReactNode;
|
|
80
|
+
/** A search bar accessory; rendered as a `Slot` child named `searchBarAccessory`. */
|
|
81
|
+
searchBarAccessory?: ReactNode;
|
|
82
|
+
}
|
|
83
|
+
export interface GridItemProps {
|
|
84
|
+
/** Passed to the `onSelectionChange` handler of the Grid when selected. */
|
|
85
|
+
id?: string;
|
|
86
|
+
/** An image or color, optionally with a tooltip, representing the content. */
|
|
87
|
+
content: GridItemContent;
|
|
88
|
+
/** An optional title displayed below the content. */
|
|
89
|
+
title?: string;
|
|
90
|
+
/** An optional subtitle displayed below the title. */
|
|
91
|
+
subtitle?: string;
|
|
92
|
+
/** Additional indexable strings for search. */
|
|
93
|
+
keywords?: string[];
|
|
94
|
+
/** An optional accessory displayed underneath the item. */
|
|
95
|
+
accessory?: GridItemAccessory;
|
|
96
|
+
/** Optional information to preview files with Quick Look. */
|
|
97
|
+
quickLook?: {
|
|
98
|
+
name?: string | null;
|
|
99
|
+
path: string;
|
|
100
|
+
};
|
|
101
|
+
/** An ActionPanel; rendered as a `Slot` child named `actions`. */
|
|
102
|
+
actions?: ReactNode;
|
|
103
|
+
}
|
|
104
|
+
export interface GridSectionProps {
|
|
105
|
+
/** The `Grid.Item` elements of the section. */
|
|
106
|
+
children?: ReactNode;
|
|
107
|
+
/** Title displayed above the section. */
|
|
108
|
+
title?: string;
|
|
109
|
+
/** An optional subtitle displayed next to the title of the section. */
|
|
110
|
+
subtitle?: string;
|
|
111
|
+
/** Column count for the section. */
|
|
112
|
+
columns?: number;
|
|
113
|
+
/** Aspect ratio for the section's `Grid.Item` elements. */
|
|
114
|
+
aspectRatio?: GridAspectRatio;
|
|
115
|
+
/** Fit for the section's `Grid.Item` element content. */
|
|
116
|
+
fit?: GridFit;
|
|
117
|
+
/** Space between the section's `Grid.Item` content and its borders. */
|
|
118
|
+
inset?: GridInset;
|
|
119
|
+
}
|
|
120
|
+
export interface GridEmptyViewProps {
|
|
121
|
+
/** The main title displayed for the empty view. */
|
|
122
|
+
title?: string;
|
|
123
|
+
/** An optional description for why the empty view is shown. */
|
|
124
|
+
description?: string;
|
|
125
|
+
/** An optional icon displayed in the center of the empty view. */
|
|
126
|
+
icon?: ImageLike | null;
|
|
127
|
+
/** An ActionPanel; rendered as a `Slot` child named `actions`. */
|
|
128
|
+
actions?: ReactNode;
|
|
129
|
+
}
|
|
130
|
+
export interface GridDropdownItemProps {
|
|
131
|
+
/** Value of the dropdown item; passed to `onChange` when selected. */
|
|
132
|
+
value: string;
|
|
133
|
+
/** The title displayed for the item. */
|
|
134
|
+
title: string;
|
|
135
|
+
/** An optional icon displayed for the item. */
|
|
136
|
+
icon?: ImageLike | null;
|
|
137
|
+
/** Additional indexable strings for search. */
|
|
138
|
+
keywords?: string[];
|
|
139
|
+
}
|
|
140
|
+
export interface GridDropdownSectionProps {
|
|
141
|
+
/** The `Grid.Dropdown.Item` elements of the section. */
|
|
142
|
+
children?: ReactNode;
|
|
143
|
+
/** Title displayed above the section. */
|
|
144
|
+
title?: string;
|
|
145
|
+
}
|
|
146
|
+
export interface GridDropdownProps {
|
|
147
|
+
/** The dropdown sections or items. */
|
|
148
|
+
children?: ReactNode;
|
|
149
|
+
/** ID of the dropdown. */
|
|
150
|
+
id?: string;
|
|
151
|
+
/** Tooltip displayed when hovering the dropdown. */
|
|
152
|
+
tooltip: string;
|
|
153
|
+
/** Placeholder text shown in the dropdown search field. */
|
|
154
|
+
placeholder?: string;
|
|
155
|
+
/**
|
|
156
|
+
* Persist the selected value per command and dropdown id across runs.
|
|
157
|
+
*/
|
|
158
|
+
storeValue?: boolean;
|
|
159
|
+
/** The current value of the dropdown. */
|
|
160
|
+
value?: string;
|
|
161
|
+
/** The default value of the dropdown, configured once per component lifecycle. */
|
|
162
|
+
defaultValue?: string;
|
|
163
|
+
/** Toggles client-side filtering of the dropdown items. */
|
|
164
|
+
filtering?: boolean | {
|
|
165
|
+
keepSectionOrder: boolean;
|
|
166
|
+
};
|
|
167
|
+
/** Indicates whether a loading indicator should be shown next to the dropdown. */
|
|
168
|
+
isLoading?: boolean;
|
|
169
|
+
/** Defines whether `onSearchTextChange` fires on every keystroke or throttled. */
|
|
170
|
+
throttle?: boolean;
|
|
171
|
+
/** Callback triggered when the dropdown search field text changes. */
|
|
172
|
+
onSearchTextChange?: (text: string) => void;
|
|
173
|
+
/** Callback triggered when the dropdown selection changes. */
|
|
174
|
+
onChange?: (newValue: string) => void;
|
|
175
|
+
}
|
|
176
|
+
declare const GridDropdownItem: (props: GridDropdownItemProps) => import("react").ReactElement<GridDropdownItemProps, string | import("react").JSXElementConstructor<any>>;
|
|
177
|
+
declare const GridDropdownSection: (props: GridDropdownSectionProps) => import("react").DOMElement<GridDropdownSectionProps, Element>;
|
|
178
|
+
declare const GridItem: (props: GridItemProps) => import("react").ReactElement<{
|
|
179
|
+
/** Passed to the `onSelectionChange` handler of the Grid when selected. */
|
|
180
|
+
id?: string;
|
|
181
|
+
/** An image or color, optionally with a tooltip, representing the content. */
|
|
182
|
+
content: GridItemContent;
|
|
183
|
+
/** An optional title displayed below the content. */
|
|
184
|
+
title?: string;
|
|
185
|
+
/** An optional subtitle displayed below the title. */
|
|
186
|
+
subtitle?: string;
|
|
187
|
+
/** Additional indexable strings for search. */
|
|
188
|
+
keywords?: string[];
|
|
189
|
+
/** An optional accessory displayed underneath the item. */
|
|
190
|
+
accessory?: GridItemAccessory;
|
|
191
|
+
/** Optional information to preview files with Quick Look. */
|
|
192
|
+
quickLook?: {
|
|
193
|
+
name?: string | null;
|
|
194
|
+
path: string;
|
|
195
|
+
};
|
|
196
|
+
}, string | import("react").JSXElementConstructor<any>>;
|
|
197
|
+
declare function GridSection(props: GridSectionProps): import("react").DOMElement<GridSectionProps, Element>;
|
|
198
|
+
declare function GridEmptyView(props: GridEmptyViewProps): import("react").ReactElement<{
|
|
199
|
+
/** The main title displayed for the empty view. */
|
|
200
|
+
title?: string;
|
|
201
|
+
/** An optional description for why the empty view is shown. */
|
|
202
|
+
description?: string;
|
|
203
|
+
/** An optional icon displayed in the center of the empty view. */
|
|
204
|
+
icon?: ImageLike | null;
|
|
205
|
+
}, string | import("react").JSXElementConstructor<any>>;
|
|
206
|
+
export declare const Grid: ((props: GridProps) => import("react").ReactElement<{
|
|
207
|
+
/** The main title for that view displayed in the host. */
|
|
208
|
+
navigationTitle?: string;
|
|
209
|
+
/** Indicates whether a loading bar should be shown below the search bar. */
|
|
210
|
+
isLoading?: boolean;
|
|
211
|
+
/** The text that will be displayed in the search bar. */
|
|
212
|
+
searchText?: string;
|
|
213
|
+
/** Placeholder text of the search bar. */
|
|
214
|
+
searchBarPlaceholder?: string;
|
|
215
|
+
/**
|
|
216
|
+
* Toggles client-side filtering of the items through the search bar; the
|
|
217
|
+
* client decides how to honor it (see task 0020, "Out of scope").
|
|
218
|
+
*/
|
|
219
|
+
filtering?: boolean | {
|
|
220
|
+
keepSectionOrder: boolean;
|
|
221
|
+
};
|
|
222
|
+
/** Deprecated; use `filtering` instead. */
|
|
223
|
+
enableFiltering?: boolean;
|
|
224
|
+
/**
|
|
225
|
+
* Defines whether the `onSearchTextChange` handler fires on every keystroke
|
|
226
|
+
* or with a throttling delay.
|
|
227
|
+
*/
|
|
228
|
+
throttle?: boolean;
|
|
229
|
+
/** Callback triggered when the search bar text changes. */
|
|
230
|
+
onSearchTextChange?: (text: string) => void;
|
|
231
|
+
/** Callback triggered when the item selection in the grid changes. */
|
|
232
|
+
onSelectionChange?: (id: string | null) => void;
|
|
233
|
+
/** Selects the item with the specified id. */
|
|
234
|
+
selectedItemId?: string;
|
|
235
|
+
/** Column count for the grid's sections. */
|
|
236
|
+
columns?: number;
|
|
237
|
+
/** Deprecated; use `columns` instead. */
|
|
238
|
+
itemSize?: GridItemSize;
|
|
239
|
+
/** Aspect ratio for the `Grid.Item` elements. */
|
|
240
|
+
aspectRatio?: GridAspectRatio;
|
|
241
|
+
/** Fit for the `Grid.Item` element content. */
|
|
242
|
+
fit?: GridFit;
|
|
243
|
+
/** Space between a `Grid.Item`'s content and its borders. */
|
|
244
|
+
inset?: GridInset;
|
|
245
|
+
/**
|
|
246
|
+
* Pagination configuration; `onLoadMore` fires when the client loads the
|
|
247
|
+
* next page (Blast records the request like any other handler).
|
|
248
|
+
*/
|
|
249
|
+
pagination?: {
|
|
250
|
+
pageSize: number;
|
|
251
|
+
hasMore: boolean;
|
|
252
|
+
onLoadMore: () => void;
|
|
253
|
+
};
|
|
254
|
+
}, string | import("react").JSXElementConstructor<any>>) & {
|
|
255
|
+
Item: typeof GridItem;
|
|
256
|
+
Section: typeof GridSection;
|
|
257
|
+
EmptyView: typeof GridEmptyView;
|
|
258
|
+
Dropdown: ((props: GridDropdownProps) => import("react").ReactElement<{
|
|
259
|
+
children?: ReactNode;
|
|
260
|
+
id?: string;
|
|
261
|
+
tooltip: string;
|
|
262
|
+
placeholder?: string;
|
|
263
|
+
storeValue?: boolean;
|
|
264
|
+
defaultValue?: string;
|
|
265
|
+
filtering?: boolean | {
|
|
266
|
+
keepSectionOrder: boolean;
|
|
267
|
+
};
|
|
268
|
+
isLoading?: boolean;
|
|
269
|
+
throttle?: boolean;
|
|
270
|
+
onSearchTextChange?: (text: string) => void;
|
|
271
|
+
value: string | undefined;
|
|
272
|
+
onChange: ((value: string) => void | undefined) | undefined;
|
|
273
|
+
}, string | import("react").JSXElementConstructor<any>>) & {
|
|
274
|
+
Item: typeof GridDropdownItem;
|
|
275
|
+
Section: typeof GridDropdownSection;
|
|
276
|
+
};
|
|
277
|
+
Inset: {
|
|
278
|
+
readonly Zero: "zero";
|
|
279
|
+
readonly Small: "sm";
|
|
280
|
+
readonly Medium: "md";
|
|
281
|
+
readonly Large: "lg";
|
|
282
|
+
};
|
|
283
|
+
ItemSize: {
|
|
284
|
+
readonly Small: "small";
|
|
285
|
+
readonly Medium: "medium";
|
|
286
|
+
readonly Large: "large";
|
|
287
|
+
};
|
|
288
|
+
Fit: {
|
|
289
|
+
readonly Contain: "contain";
|
|
290
|
+
readonly Fill: "fill";
|
|
291
|
+
};
|
|
292
|
+
};
|
|
293
|
+
export {};
|
package/dist/api/grid.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createElement } from "react";
|
|
2
|
+
import { useDropdown } from "./dropdown.js";
|
|
3
|
+
import { GridFit, GridInset, GridItemSize } from "./enums.generated.js";
|
|
4
|
+
import { slot } from "./slot.js";
|
|
5
|
+
const GridDropdownItem = function GridDropdownItem(props) {
|
|
6
|
+
return createElement("Grid.Dropdown.Item", props);
|
|
7
|
+
};
|
|
8
|
+
const GridDropdownSection = function GridDropdownSection(props) {
|
|
9
|
+
return createElement("Grid.Dropdown.Section", props);
|
|
10
|
+
};
|
|
11
|
+
const GridDropdown = Object.assign(function GridDropdown(props) {
|
|
12
|
+
return useDropdown("Grid.Dropdown", props);
|
|
13
|
+
}, { Item: GridDropdownItem, Section: GridDropdownSection });
|
|
14
|
+
const GridItem = function GridItem(props) {
|
|
15
|
+
const { actions, ...rest } = props;
|
|
16
|
+
return createElement("Grid.Item", rest, slot("actions", actions));
|
|
17
|
+
};
|
|
18
|
+
function GridSection(props) {
|
|
19
|
+
return createElement("Grid.Section", props);
|
|
20
|
+
}
|
|
21
|
+
function GridEmptyView(props) {
|
|
22
|
+
const { actions, ...rest } = props;
|
|
23
|
+
return createElement("Grid.EmptyView", rest, slot("actions", actions));
|
|
24
|
+
}
|
|
25
|
+
export const Grid = Object.assign(function Grid(props) {
|
|
26
|
+
const { actions, searchBarAccessory, children, ...rest } = props;
|
|
27
|
+
return createElement("Grid", rest, children, slot("actions", actions), slot("searchBarAccessory", searchBarAccessory));
|
|
28
|
+
}, {
|
|
29
|
+
Item: GridItem,
|
|
30
|
+
Section: GridSection,
|
|
31
|
+
EmptyView: GridEmptyView,
|
|
32
|
+
Dropdown: GridDropdown,
|
|
33
|
+
Inset: GridInset,
|
|
34
|
+
ItemSize: GridItemSize,
|
|
35
|
+
Fit: GridFit,
|
|
36
|
+
});
|