@pipelex/mthds-form 0.13.0 → 0.14.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 +11 -0
- package/dist/{chunk-IR3OFSGD.js → chunk-2EGRONBV.js} +3 -3
- package/dist/{chunk-IR3OFSGD.js.map → chunk-2EGRONBV.js.map} +1 -1
- package/dist/{chunk-D2EQVWGN.js → chunk-7JRT6ZII.js} +1631 -171
- package/dist/chunk-7JRT6ZII.js.map +1 -0
- package/dist/{chunk-7KQXGG6J.js → chunk-ICUEHNEH.js} +19 -6
- package/dist/chunk-ICUEHNEH.js.map +1 -0
- package/dist/{chunk-DARLDVJM.js → chunk-JIGRWJTV.js} +2 -2
- package/dist/chunk-JIGRWJTV.js.map +1 -0
- package/dist/chunk-VH5CDQDR.js +41 -0
- package/dist/chunk-VH5CDQDR.js.map +1 -0
- package/dist/{chunk-G6SFMTL4.js → chunk-VT5E5W4E.js} +4 -3
- package/dist/chunk-VT5E5W4E.js.map +1 -0
- package/dist/{chunk-MJQ7CXUB.js → chunk-XO5OENNF.js} +25 -4
- package/dist/chunk-XO5OENNF.js.map +1 -0
- package/dist/core/gate.js +2 -2
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.js +6 -5
- package/dist/core/output-form.d.ts +5 -4
- package/dist/core/output-form.js +1 -1
- package/dist/core/readiness.d.ts +47 -2
- package/dist/core/readiness.js +1 -1
- package/dist/core/save-plan.d.ts +31 -1
- package/dist/core/save-plan.js +1 -1
- package/dist/core/seed.d.ts +56 -0
- package/dist/core/seed.js +5 -0
- package/dist/core/seed.js.map +1 -0
- package/dist/core/values.js +2 -2
- package/dist/generative/index.d.ts +2 -11
- package/dist/generative/index.js +11 -17
- package/dist/generative/index.js.map +1 -1
- package/dist/react/index.d.ts +699 -192
- package/dist/react/index.js +9 -3
- package/dist/styles.css +1 -1
- package/package.json +3 -1
- package/dist/chunk-7KQXGG6J.js.map +0 -1
- package/dist/chunk-D2EQVWGN.js.map +0 -1
- package/dist/chunk-DARLDVJM.js.map +0 -1
- package/dist/chunk-G6SFMTL4.js.map +0 -1
- package/dist/chunk-MJQ7CXUB.js.map +0 -1
package/dist/react/index.d.ts
CHANGED
|
@@ -3,11 +3,12 @@ export { a as FieldRenderer, b as FieldRendererProps } from '../field-renderer-C
|
|
|
3
3
|
import * as react from 'react';
|
|
4
4
|
import { ReactNode } from 'react';
|
|
5
5
|
import { BooleanRunField, DateRunField, EnumRunField, FileRunField, ListRunField, NumberRunField, ObjectRunField, ProseRunField, TextRunField, UnknownRunField, RunField, ConceptCategory } from '../core/descriptor.js';
|
|
6
|
+
import { PipeInputFormDescriptor, PipeIOContract } from 'mthds/protocol';
|
|
7
|
+
import { Readiness } from '../core/readiness.js';
|
|
6
8
|
import { SaveFiles, SaveFile, SaveResult } from '../core/save-plan.js';
|
|
7
9
|
import { StuffFileKind } from '../core/stuff-files.js';
|
|
8
10
|
import { HtmlContentView } from '../core/native-content.js';
|
|
9
11
|
import '../core/contracts.js';
|
|
10
|
-
import 'mthds/protocol';
|
|
11
12
|
import '../core/file-formats.js';
|
|
12
13
|
|
|
13
14
|
interface BooleanFieldProps {
|
|
@@ -164,6 +165,14 @@ interface ObjectFieldProps {
|
|
|
164
165
|
* A structured concept: its sub-fields rendered inside one hairline-grouped
|
|
165
166
|
* card so nesting stays legible. Empty optional sub-fields collapse behind a
|
|
166
167
|
* "+ N optional" toggle - required and already-filled fields always show.
|
|
168
|
+
*
|
|
169
|
+
* A CLOSED optional sub-structure (absent value) folds behind that toggle and
|
|
170
|
+
* is counted like any empty optional field. The toggle also opens and closes
|
|
171
|
+
* them: expanding writes `seedObjectValue` into every closed one, collapsing
|
|
172
|
+
* writes `undefined` into every one, so they leave the payload. It reads
|
|
173
|
+
* expanded while any of them is open, so a restored value that holds one
|
|
174
|
+
* offers to close it. An open optional structure renders like any other: its
|
|
175
|
+
* label, description and card, with no badge.
|
|
167
176
|
*/
|
|
168
177
|
declare function ObjectField({ field, value, onChange, id, error, env }: ObjectFieldProps): react.JSX.Element;
|
|
169
178
|
|
|
@@ -207,6 +216,381 @@ interface UnknownFieldProps {
|
|
|
207
216
|
*/
|
|
208
217
|
declare function UnknownField({ field, value, onChange, id, error, disabled }: UnknownFieldProps): react.JSX.Element;
|
|
209
218
|
|
|
219
|
+
/**
|
|
220
|
+
* How a field's LABEL chrome, and the enum values a person reads, should read.
|
|
221
|
+
* What a control stores never changes.
|
|
222
|
+
*
|
|
223
|
+
* - `studio` (default): the field's name verbatim in mono, with its concept
|
|
224
|
+
* pill. The name IS the identifier a builder writes in the `.mthds` file, so
|
|
225
|
+
* showing it exactly - and typed - is the point. An enum value is shown as its
|
|
226
|
+
* code for the same reason: it has to match the bundle and the JSON the
|
|
227
|
+
* builder reads next.
|
|
228
|
+
* - `app`: the field's name as a human question in sans, no concept pill. Inside
|
|
229
|
+
* a method app, `full_name` and `native.Text` are implementation detail; the
|
|
230
|
+
* person filling the form has never seen the method's source. An enum value
|
|
231
|
+
* follows the label and reads as words.
|
|
232
|
+
*
|
|
233
|
+
* An enum value is the only value the two presentations show differently. A
|
|
234
|
+
* number, a text, a date and every other value render the same in both.
|
|
235
|
+
*
|
|
236
|
+
* Deliberately a context rather than a prop or a `FieldEnv` flag: only the
|
|
237
|
+
* components that own label chrome or an enum value's wording read it -
|
|
238
|
+
* `FieldShell`, `ObjectField`, `BooleanField`, `ListField`, `EnumField` and the
|
|
239
|
+
* result view - plus the file control, whose card hides a storage reference in
|
|
240
|
+
* `app`, and threading a prop would have meant editing every field control and
|
|
241
|
+
* both recursive containers.
|
|
242
|
+
*/
|
|
243
|
+
type FieldPresentation = 'studio' | 'app';
|
|
244
|
+
declare function FieldPresentationProvider({ presentation, children, }: {
|
|
245
|
+
presentation: FieldPresentation;
|
|
246
|
+
children: ReactNode;
|
|
247
|
+
}): react.JSX.Element;
|
|
248
|
+
declare function useFieldPresentation(): FieldPresentation;
|
|
249
|
+
/**
|
|
250
|
+
* `full_name` → `Full name`. Only the first word is capitalised: these are
|
|
251
|
+
* labels, not titles, and Title Case on every word reads as a form from 2009.
|
|
252
|
+
* Takes IDENTIFIERS only - snake_case or kebab-case names, where a separator
|
|
253
|
+
* is structure. An authored `title` never goes through here: it is already
|
|
254
|
+
* human-facing, and its hyphens are spelling (`E-mail address`), not structure.
|
|
255
|
+
*/
|
|
256
|
+
declare function humanizeFieldName(name: string): string;
|
|
257
|
+
|
|
258
|
+
interface FieldStrings {
|
|
259
|
+
/** Badge on an optional field's header row. */
|
|
260
|
+
optionalBadge: string;
|
|
261
|
+
selectPlaceholder: string;
|
|
262
|
+
typeValuePlaceholder: string;
|
|
263
|
+
writeHerePlaceholder: string;
|
|
264
|
+
/** Helper under the raw-JSON escape hatch. */
|
|
265
|
+
jsonHint: string;
|
|
266
|
+
decrease: string;
|
|
267
|
+
increase: string;
|
|
268
|
+
noItemsYet: string;
|
|
269
|
+
addItem: string;
|
|
270
|
+
removeItemAria: (index: number) => string;
|
|
271
|
+
itemsCount: (count: number) => string;
|
|
272
|
+
/** The same badge for a list the method gave an exact count (`Concept[N]`). */
|
|
273
|
+
itemsCountOf: (count: number, total: number) => string;
|
|
274
|
+
/** What a record's table cell says when none of its fields can name it. */
|
|
275
|
+
fieldsCount: (count: number) => string;
|
|
276
|
+
uploading: string;
|
|
277
|
+
dropToUpload: string;
|
|
278
|
+
dropOrBrowse: string;
|
|
279
|
+
pasteUrlInstead: string;
|
|
280
|
+
/**
|
|
281
|
+
* What a file field says in place of its dropzone when its host has no way to
|
|
282
|
+
* store a file, so a link is the only way in. The field's format hint follows
|
|
283
|
+
* it, because the link still has to point at a file the runtime can read.
|
|
284
|
+
*/
|
|
285
|
+
uploadUnavailable: string;
|
|
286
|
+
/**
|
|
287
|
+
* The placeholder in the URL a person may paste instead of uploading. It names
|
|
288
|
+
* the web's own scheme only: a host whose runner also takes its own storage
|
|
289
|
+
* references can say so by overriding it, and no default shows an end user a
|
|
290
|
+
* platform's storage scheme.
|
|
291
|
+
*/
|
|
292
|
+
urlPlaceholder: string;
|
|
293
|
+
/**
|
|
294
|
+
* The accessible name of that URL input, given the field's label as the form
|
|
295
|
+
* shows it. The field's label is bound to the file input, so without this the
|
|
296
|
+
* placeholder was the only thing a screen reader announced here.
|
|
297
|
+
*
|
|
298
|
+
* The label is EMPTY inside a list row: the list drops each row's label, and
|
|
299
|
+
* the row number it shows instead is a bare glyph tied to no control. So every
|
|
300
|
+
* row's input currently carries the same generic name, and giving rows
|
|
301
|
+
* distinct names is a separate piece of work on the list control.
|
|
302
|
+
*/
|
|
303
|
+
fileUrlAria: (label: string) => string;
|
|
304
|
+
/**
|
|
305
|
+
* The title of an attached file's card when the value carries no filename.
|
|
306
|
+
* True of a pasted link as well as of an upload, which "Uploaded file" was not.
|
|
307
|
+
*/
|
|
308
|
+
uploadedFile: string;
|
|
309
|
+
/**
|
|
310
|
+
* What names a file carried INSIDE its own URL (a `data:` URL), where a
|
|
311
|
+
* reference would otherwise be printed: its format and its decoded size. The
|
|
312
|
+
* string itself is forty thousand characters of base64 and says nothing.
|
|
313
|
+
*/
|
|
314
|
+
encodedFileSummary: (format: string, bytes: number) => string;
|
|
315
|
+
preview: string;
|
|
316
|
+
removeFileAria: string;
|
|
317
|
+
previewUnavailablePdf: string;
|
|
318
|
+
/**
|
|
319
|
+
* What a result slot the run resolved as an absence SHOWS — a dash, not a
|
|
320
|
+
* sentence. Beside forty values in a grid, "not provided" repeated is louder
|
|
321
|
+
* than the data; a dash reads as the blank it is.
|
|
322
|
+
*/
|
|
323
|
+
resultAbsent: string;
|
|
324
|
+
/**
|
|
325
|
+
* What that dash SAYS, to a screen reader.
|
|
326
|
+
*
|
|
327
|
+
* The two are separate because a glyph and a sentence are answers to different
|
|
328
|
+
* questions. A table column of hyphens read aloud is "hyphen, hyphen, hyphen",
|
|
329
|
+
* which is noise rather than information — so the dash is `aria-hidden` and
|
|
330
|
+
* this rides beside it, visually hidden.
|
|
331
|
+
*/
|
|
332
|
+
resultAbsentDescription: string;
|
|
333
|
+
/**
|
|
334
|
+
* Shown above the raw value when a result cannot be laid out because the
|
|
335
|
+
* artifacts describing it are absent. It names the CAUSE — the descriptor,
|
|
336
|
+
* not the data — because the value is right there underneath and a reader
|
|
337
|
+
* who is not told why will assume the view is broken.
|
|
338
|
+
*/
|
|
339
|
+
resultUndescribed: string;
|
|
340
|
+
/** The control that opens a result table row to show the rest of the record. */
|
|
341
|
+
toggleRowDetails: (index: number) => string;
|
|
342
|
+
/** The (visually hidden) header of the column those controls sit in. */
|
|
343
|
+
rowDetailsColumn: string;
|
|
344
|
+
/** The control that puts a file's whole URL on the clipboard. */
|
|
345
|
+
copyUrl: string;
|
|
346
|
+
/** The copy control beside any text value, markdown or not. */
|
|
347
|
+
copyText: string;
|
|
348
|
+
/** The download control's label. */
|
|
349
|
+
download: string;
|
|
350
|
+
/** Announced while the files are being fetched. */
|
|
351
|
+
downloading: string;
|
|
352
|
+
/**
|
|
353
|
+
* What the whole-result download says when some files did not arrive: the
|
|
354
|
+
* ones no URL could be found for, and the ones the delivery reported. Takes
|
|
355
|
+
* the names the files would have been saved under.
|
|
356
|
+
*/
|
|
357
|
+
downloadIncomplete: (names: readonly string[]) => string;
|
|
358
|
+
/**
|
|
359
|
+
* The button beside each file that saves that one file, given the name it
|
|
360
|
+
* saves under: a gallery of buttons all called "Download" would leave a
|
|
361
|
+
* screen-reader user unable to tell which file each one saves.
|
|
362
|
+
*/
|
|
363
|
+
downloadFile: (name: string) => string;
|
|
364
|
+
/** What that button says when the file did not arrive. */
|
|
365
|
+
downloadFileFailed: string;
|
|
366
|
+
/** The control on an HTML page that saves it as a PDF, in one click. */
|
|
367
|
+
downloadPdf: string;
|
|
368
|
+
/** What that control says while the PDF is being made. */
|
|
369
|
+
downloadPdfBusy: string;
|
|
370
|
+
/** What that control says when the PDF could not be made or saved. */
|
|
371
|
+
downloadPdfFailed: string;
|
|
372
|
+
/**
|
|
373
|
+
* Shown in place of an HTML page for the moment the stored pictures it names
|
|
374
|
+
* are being resolved through the host.
|
|
375
|
+
*/
|
|
376
|
+
pageLoading: string;
|
|
377
|
+
/**
|
|
378
|
+
* The page's own HTML download, beside "Download PDF" on a result that is
|
|
379
|
+
* one page, where a bare "Download" would not say which of the two it saves.
|
|
380
|
+
*/
|
|
381
|
+
downloadHtml: string;
|
|
382
|
+
/**
|
|
383
|
+
* The dialog that asks for a download's name, when the host turns
|
|
384
|
+
* `downloads.askFileName` on: its title, which also names the field, and its
|
|
385
|
+
* two buttons.
|
|
386
|
+
*/
|
|
387
|
+
fileNameTitle: string;
|
|
388
|
+
fileNameCancel: string;
|
|
389
|
+
fileNameConfirm: string;
|
|
390
|
+
/** The result panel's two views, and the control that copies the payload. */
|
|
391
|
+
viewRendered: string;
|
|
392
|
+
viewJson: string;
|
|
393
|
+
copyJson: string;
|
|
394
|
+
resultViewGroup: string;
|
|
395
|
+
yes: string;
|
|
396
|
+
no: string;
|
|
397
|
+
/**
|
|
398
|
+
* Shown when a picked file is not a format this slot accepts. Takes the
|
|
399
|
+
* slot's own formats as the hint names them (`PNG, JPG`), after any narrowing
|
|
400
|
+
* a host applied, so the message names what WOULD have worked - "not
|
|
401
|
+
* accepted" on its own leaves the user guessing at the list they just failed.
|
|
402
|
+
*/
|
|
403
|
+
unsupportedFileType: (formats: string) => string;
|
|
404
|
+
/** The optional-entries disclosure ("field" inside a concept, "input" at top level). */
|
|
405
|
+
hideOptionalFields: string;
|
|
406
|
+
hideOptionalInputs: string;
|
|
407
|
+
optionalFieldsCount: (count: number) => string;
|
|
408
|
+
optionalInputsCount: (count: number) => string;
|
|
409
|
+
/**
|
|
410
|
+
* The mark `MethodForm` puts on an input the run needs and does not have,
|
|
411
|
+
* once the person has tried to run.
|
|
412
|
+
*/
|
|
413
|
+
requiredField: string;
|
|
414
|
+
/**
|
|
415
|
+
* The mark on an optional input that was started but is not complete, once
|
|
416
|
+
* the person has tried to run: a started structure owes its concept every
|
|
417
|
+
* required field.
|
|
418
|
+
*/
|
|
419
|
+
incompleteField: string;
|
|
420
|
+
/** What a file field says when the host's `uploadFile` rejected its file. */
|
|
421
|
+
uploadFailed: string;
|
|
422
|
+
}
|
|
423
|
+
/** The English strings, which every control falls back to. Kept under this name for existing hosts. */
|
|
424
|
+
declare const DEFAULT_FIELD_STRINGS: FieldStrings;
|
|
425
|
+
/**
|
|
426
|
+
* Set the strings every control below speaks. `locale` picks a language pack by its code (`fr`,
|
|
427
|
+
* `fr-FR`, `en-GB`…; an unknown one falls back to English), and `strings` overrides single keys
|
|
428
|
+
* on top of it. Both are optional: with neither, the controls speak English.
|
|
429
|
+
*/
|
|
430
|
+
declare function FieldStringsProvider({ locale, strings, children, }: {
|
|
431
|
+
locale?: string;
|
|
432
|
+
strings?: Partial<FieldStrings>;
|
|
433
|
+
children: ReactNode;
|
|
434
|
+
}): react.JSX.Element;
|
|
435
|
+
/** The strings the nearest provider supplies (English defaults when none does). */
|
|
436
|
+
declare function useFieldStrings(): FieldStrings;
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* A whole method's input form: every input of one pipe, built from its
|
|
440
|
+
* input-form descriptor and its contract, with the state a host would
|
|
441
|
+
* otherwise hand-write around `FieldRenderer` - the seed, the uploads, the
|
|
442
|
+
* top-level optional disclosure, the required marks and the run gate.
|
|
443
|
+
*
|
|
444
|
+
* It is two pieces, so the consumer keeps what is its own. `useMethodForm`
|
|
445
|
+
* holds the state and hands back the values, the run inputs and whether they
|
|
446
|
+
* are ready, wherever in the host's tree it is called - typically beside the
|
|
447
|
+
* run button. `MethodForm` renders the fields for that state and nothing else:
|
|
448
|
+
* no panel, no heading, no run button, no history, no `<form>` element. See
|
|
449
|
+
* docs/method-form.md.
|
|
450
|
+
*/
|
|
451
|
+
/** The form's values, keyed by input name, in the controls' own shape. */
|
|
452
|
+
type MethodFormValues = Record<string, unknown>;
|
|
453
|
+
/** What a host's upload resolves to: the stored file's URL, or its URL and name. */
|
|
454
|
+
type UploadedFile = string | {
|
|
455
|
+
url: string;
|
|
456
|
+
filename?: string;
|
|
457
|
+
};
|
|
458
|
+
/** Where a dropped file goes: its field's id, which is its value path, split. */
|
|
459
|
+
interface MethodFormUploadContext {
|
|
460
|
+
/** The field's id, a dotted value path (`photos.1`, `invoice.source`). */
|
|
461
|
+
id: string;
|
|
462
|
+
/** The same path, split: what `setValueAtPath` takes. */
|
|
463
|
+
path: string[];
|
|
464
|
+
}
|
|
465
|
+
interface UseMethodFormOptions {
|
|
466
|
+
/** The pipe's input-form descriptor (`getPipeInputForm`). */
|
|
467
|
+
descriptor: PipeInputFormDescriptor;
|
|
468
|
+
/** The pipe's entry in `pipe_io_contracts` (`getPipeIOContract`). */
|
|
469
|
+
contract: PipeIOContract;
|
|
470
|
+
/**
|
|
471
|
+
* A transform over the derived fields, applied once before anything else
|
|
472
|
+
* reads them: narrowing a file slot (`narrowFileFormats`), dropping a child
|
|
473
|
+
* the host never sends. Pass a stable function (module-level or memoized):
|
|
474
|
+
* the fields are re-derived when it changes.
|
|
475
|
+
*/
|
|
476
|
+
prepareFields?: (fields: RunField[]) => RunField[];
|
|
477
|
+
/**
|
|
478
|
+
* Input names to show first, in this order. Every input not named keeps its
|
|
479
|
+
* descriptor order after them; a name no input has is ignored.
|
|
480
|
+
*/
|
|
481
|
+
order?: readonly string[];
|
|
482
|
+
/**
|
|
483
|
+
* What the form holds until its first edit. Absent or `null`, the method's
|
|
484
|
+
* own seed (`seedInputs`: the authored defaults, every optional structure
|
|
485
|
+
* closed). A function receives that seed and returns the values, for a host
|
|
486
|
+
* that lays something the method cannot know over it (today's date). Read on
|
|
487
|
+
* every render until the first edit, so it may change after hydration; from
|
|
488
|
+
* the first edit the form owns its values, and `reset` goes back to it.
|
|
489
|
+
*/
|
|
490
|
+
initialValues?: MethodFormValues | ((seed: MethodFormValues) => MethodFormValues) | null;
|
|
491
|
+
/**
|
|
492
|
+
* Stores a dropped or picked file and resolves to what the field holds. Its
|
|
493
|
+
* presence is the capability: without it every file field is link-only (see
|
|
494
|
+
* docs/upload-seam.md). A rejection marks the field's upload as failed with
|
|
495
|
+
* `FieldStrings.uploadFailed`; the next file dropped there clears it. When
|
|
496
|
+
* it resolves to a bare URL, or omits the name, the file's own name is kept.
|
|
497
|
+
*/
|
|
498
|
+
uploadFile?: (file: File, context: MethodFormUploadContext) => Promise<UploadedFile>;
|
|
499
|
+
}
|
|
500
|
+
/** What the form says to its consumer: the values, the run inputs, the gate. */
|
|
501
|
+
interface MethodFormState {
|
|
502
|
+
/** The fields the form renders, derived, prepared and ordered. */
|
|
503
|
+
fields: RunField[];
|
|
504
|
+
/** The current values, in the controls' shape. */
|
|
505
|
+
values: MethodFormValues;
|
|
506
|
+
/**
|
|
507
|
+
* The values as a run takes them: each input as `{ concept, content }`, an
|
|
508
|
+
* unfilled optional input left out, an empty plural input sent as `[]`
|
|
509
|
+
* (`apiInputsFromRunValues`). What a run call's `inputs` takes.
|
|
510
|
+
*/
|
|
511
|
+
inputs: Record<string, unknown>;
|
|
512
|
+
/** How many of the inputs that gate a run are filled, and which are not. */
|
|
513
|
+
readiness: Readiness;
|
|
514
|
+
/** Whether a file upload is still in flight. */
|
|
515
|
+
uploading: boolean;
|
|
516
|
+
/** Whether a run may start: every gating input filled and no upload in flight. */
|
|
517
|
+
ready: boolean;
|
|
518
|
+
/** Whether `attempt` has been called since the last reset: required marks show. */
|
|
519
|
+
attempted: boolean;
|
|
520
|
+
}
|
|
521
|
+
/** The handle `useMethodForm` returns, and `MethodForm` renders. */
|
|
522
|
+
interface MethodFormController extends MethodFormState {
|
|
523
|
+
/** Replace the values, or update them from the previous ones. */
|
|
524
|
+
setValues: (next: MethodFormValues | ((previous: MethodFormValues) => MethodFormValues)) => void;
|
|
525
|
+
/**
|
|
526
|
+
* Back to `values` (a past run's inputs, say), or to the initial values when
|
|
527
|
+
* none is given. Forgets the edits, the attempt, the failed uploads and any
|
|
528
|
+
* upload still in flight, whose result is then dropped.
|
|
529
|
+
*/
|
|
530
|
+
reset: (values?: MethodFormValues | null) => void;
|
|
531
|
+
/**
|
|
532
|
+
* The run button's press: marks the attempt, so every gating input still
|
|
533
|
+
* empty shows its required mark, and answers whether the run may start.
|
|
534
|
+
*/
|
|
535
|
+
attempt: () => boolean;
|
|
536
|
+
/** Ids (value paths) with an upload in flight. Read by `MethodForm`. */
|
|
537
|
+
uploadingIds: ReadonlySet<string>;
|
|
538
|
+
/** Ids whose last upload failed. Read by `MethodForm`. */
|
|
539
|
+
failedUploadIds: ReadonlySet<string>;
|
|
540
|
+
/** The upload seam `MethodForm` hands its file fields, when the host uploads. */
|
|
541
|
+
dropFile: ((id: string, file: File) => void) | undefined;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* The state of one method's input form. See `UseMethodFormOptions` for what it
|
|
545
|
+
* takes and `MethodFormController` for what it gives back; render it with
|
|
546
|
+
* `<MethodForm form={...} />`.
|
|
547
|
+
*
|
|
548
|
+
* A different method is a different form: remount (a new `key`) when the
|
|
549
|
+
* descriptor changes, as values belong to the inputs they were typed for.
|
|
550
|
+
*/
|
|
551
|
+
declare function useMethodForm({ descriptor, contract, prepareFields, order, initialValues, uploadFile, }: UseMethodFormOptions): MethodFormController;
|
|
552
|
+
interface MethodFormProps {
|
|
553
|
+
/** The state to render, from `useMethodForm`. */
|
|
554
|
+
form: MethodFormController;
|
|
555
|
+
/** Holds every control read-only, as a host does while a run is in flight. */
|
|
556
|
+
disabled?: boolean;
|
|
557
|
+
/** `FieldEnv.allowUrl`: `false` takes the "paste a URL instead" link away. */
|
|
558
|
+
allowUrl?: boolean;
|
|
559
|
+
/** `FieldEnv.resolveUrl`: turns a stored reference into a URL a preview can load. */
|
|
560
|
+
resolveUrl?: (uri: string) => Promise<string | null>;
|
|
561
|
+
/**
|
|
562
|
+
* Whether the empty optional inputs fold behind a "+ N optional inputs"
|
|
563
|
+
* disclosure (the default), as they do inside a structure. `false` shows
|
|
564
|
+
* every input; a closed optional structure still folds, since the
|
|
565
|
+
* disclosure is what opens it.
|
|
566
|
+
*/
|
|
567
|
+
foldOptional?: boolean;
|
|
568
|
+
/**
|
|
569
|
+
* A message per input name, shown on that input over the form's own marks:
|
|
570
|
+
* a server gate's verdict, say.
|
|
571
|
+
*/
|
|
572
|
+
errors?: Readonly<Record<string, string>>;
|
|
573
|
+
/** Mounts `FieldStringsProvider` with this locale (`fr`, `fr-FR`…). */
|
|
574
|
+
locale?: string;
|
|
575
|
+
/** Mounts `FieldStringsProvider` with these keys over the locale's pack. */
|
|
576
|
+
strings?: Partial<FieldStrings>;
|
|
577
|
+
/** Mounts `FieldPresentationProvider`; absent, the nearest one applies. */
|
|
578
|
+
presentation?: FieldPresentation;
|
|
579
|
+
/** Classes for the stack the fields sit in. */
|
|
580
|
+
className?: string;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Every input of the method `form` holds, rendered through `FieldRenderer`.
|
|
584
|
+
*
|
|
585
|
+
* Each input's id is its name, so a file's upload id is its value path. A
|
|
586
|
+
* gating input still empty after `form.attempt()` is marked `requiredField`,
|
|
587
|
+
* and an optional input started but not complete `incompleteField`; a host's
|
|
588
|
+
* `errors` entry replaces either. The top-level "+ N optional inputs"
|
|
589
|
+
* disclosure folds and opens exactly as the one inside a structure does,
|
|
590
|
+
* optional structures included.
|
|
591
|
+
*/
|
|
592
|
+
declare function MethodForm({ locale, strings, presentation, ...props }: MethodFormProps): react.ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
593
|
+
|
|
210
594
|
interface ResultFieldProps {
|
|
211
595
|
field: RunField;
|
|
212
596
|
value: unknown;
|
|
@@ -228,6 +612,71 @@ interface ResultFieldProps {
|
|
|
228
612
|
*/
|
|
229
613
|
declare function ResultField(props: ResultFieldProps): react.JSX.Element;
|
|
230
614
|
|
|
615
|
+
/**
|
|
616
|
+
* The `pipelex-storage://` references inside an HTML page, exchanged for URLs a
|
|
617
|
+
* browser can load before the page is framed, rasterised or saved.
|
|
618
|
+
*
|
|
619
|
+
* A method that renders a page (a quote, an invoice) embeds the pictures it
|
|
620
|
+
* produced as `<img src="pipelex-storage://…">`. The file arms of the result
|
|
621
|
+
* view hand such a reference to the host's resolver, but a page is markup, and
|
|
622
|
+
* nothing inside it went through that seam: the frame's policy refuses the
|
|
623
|
+
* scheme, so every picture showed as a broken image, and a host had to rewrite
|
|
624
|
+
* the markup itself before handing it over. Here it is done once, for every
|
|
625
|
+
* place a page goes: the preview, the PDF and the HTML download.
|
|
626
|
+
*
|
|
627
|
+
* ## What is read, and how
|
|
628
|
+
*
|
|
629
|
+
* The markup is parsed with `DOMParser`, never searched with a pattern, because
|
|
630
|
+
* an attribute value a regex finds may be inside a comment, a `<textarea>` or
|
|
631
|
+
* another attribute, and an `<img>` it misses may be spelled with single
|
|
632
|
+
* quotes, no quotes or an uppercase tag. A `DOMParser` document is inert: no
|
|
633
|
+
* script runs and nothing is fetched while it is read. A whole document is
|
|
634
|
+
* parsed as one; a fragment is parsed as a `<template>`'s content, which keeps
|
|
635
|
+
* a leading `<style>` or `<meta>` where it was rather than moving it into a
|
|
636
|
+
* head the fragment never had.
|
|
637
|
+
*
|
|
638
|
+
* The references read are the ones a page fetches as a picture: `src` and
|
|
639
|
+
* `srcset` on `<img>` and `<source>`, a `<video>`'s `poster`, the legacy
|
|
640
|
+
* `background` attribute, an SVG `<image>`'s `href`, and every CSS `url()` in a
|
|
641
|
+
* `style` attribute or a `<style>` element (inside CSS, which is text, the
|
|
642
|
+
* `url()` tokens are found with a pattern; the HTML around them never is).
|
|
643
|
+
*
|
|
644
|
+
* ## How each reference is answered
|
|
645
|
+
*
|
|
646
|
+
* Every distinct reference is asked for once. When the host gave a bulk
|
|
647
|
+
* resolver (`resolveUrls`), all of them go in one call, which is one round trip
|
|
648
|
+
* for a page with twenty pictures; its answer is then checked by the URL gate
|
|
649
|
+
* (`viewableUrl`), as every resolver's answer is. A reference the bulk answer
|
|
650
|
+
* left out, or answered with something the gate refuses, is asked of the
|
|
651
|
+
* per-reference resolver (`resolveUrl`) next. A reference neither answers is
|
|
652
|
+
* left as it was: the picture stays broken, and the page still shows.
|
|
653
|
+
*
|
|
654
|
+
* A bulk resolver that rejects, throws or takes longer than `timeoutMs` is
|
|
655
|
+
* read as having answered nothing, so a page never waits on it for good.
|
|
656
|
+
*
|
|
657
|
+
* ## What comes back
|
|
658
|
+
*
|
|
659
|
+
* The markup with each answered reference replaced, serialised from the parsed
|
|
660
|
+
* tree. Markup holding no reference that changed comes back as the very same
|
|
661
|
+
* string, so a page with no stored pictures is never re-serialised.
|
|
662
|
+
*/
|
|
663
|
+
/** Answers a batch of stored references; see `ResultEnvProvider`'s `resolveUrls`. */
|
|
664
|
+
type ResolveUrls = (uris: readonly string[]) => Promise<ReadonlyMap<string, string | null | undefined> | Readonly<Record<string, string | null | undefined>>>;
|
|
665
|
+
interface MarkupResolvers {
|
|
666
|
+
/** One reference at a time, synchronously, as the result view's file arms ask. */
|
|
667
|
+
resolveUrl?: (url: string) => string | undefined;
|
|
668
|
+
/** Every reference of the page in one call. Asked first when present. */
|
|
669
|
+
resolveUrls?: ResolveUrls;
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* The markup with every stored reference its pictures name resolved through
|
|
673
|
+
* the host's resolvers; see the module comment for the order they are asked in.
|
|
674
|
+
* Never rejects, and never waits on a bulk resolver past `timeoutMs`.
|
|
675
|
+
*/
|
|
676
|
+
declare function resolveHtmlStorageUrls(markup: string, { resolveUrl, resolveUrls }: MarkupResolvers, { timeoutMs }?: {
|
|
677
|
+
timeoutMs?: number;
|
|
678
|
+
}): Promise<string>;
|
|
679
|
+
|
|
231
680
|
/**
|
|
232
681
|
* The host's seam for turning a stored reference into something a browser can
|
|
233
682
|
* fetch.
|
|
@@ -329,9 +778,47 @@ interface DownloadDisplay {
|
|
|
329
778
|
* `'document'`, and `'markup'` for an HTML page.
|
|
330
779
|
*/
|
|
331
780
|
files?: boolean | readonly StuffFileKind[];
|
|
781
|
+
/**
|
|
782
|
+
* The "Download PDF" control on each HTML page, which makes a PDF of the
|
|
783
|
+
* page in the browser and saves it. Drawn unless `false`.
|
|
784
|
+
*/
|
|
785
|
+
pdf?: boolean;
|
|
786
|
+
/**
|
|
787
|
+
* Ask the reader for the file's name before each download, in a dialog
|
|
788
|
+
* pre-filled with the name the plan chose. Off unless `true`.
|
|
789
|
+
*
|
|
790
|
+
* It is a behaviour of every download control rather than a control of its
|
|
791
|
+
* own, which is why it sits beside the three that say which are drawn: the
|
|
792
|
+
* PDF, the page's HTML, a file's own button, and the whole-result download.
|
|
793
|
+
* A whole-result download saving one file asks for that file's name; one
|
|
794
|
+
* saving several asks for the name they share, the base name, which names
|
|
795
|
+
* the JSON copy and every file the payload did not name itself.
|
|
796
|
+
*/
|
|
797
|
+
askFileName?: boolean;
|
|
332
798
|
}
|
|
333
799
|
interface ResultEnv {
|
|
334
800
|
resolveUrl?: ResolveUrl;
|
|
801
|
+
/**
|
|
802
|
+
* Every stored reference an HTML page's pictures name, resolved in one call.
|
|
803
|
+
*
|
|
804
|
+
* A `native.Html` page embeds the pictures its method produced as
|
|
805
|
+
* `pipelex-storage://` references (`<img src>`, `srcset`, a CSS `url()`),
|
|
806
|
+
* which the frame cannot load. Before the page is shown, made into a PDF or
|
|
807
|
+
* downloaded as HTML, the view collects the distinct references it holds and
|
|
808
|
+
* hands them here, once, and writes each URL this answers into the page.
|
|
809
|
+
*
|
|
810
|
+
* Async and BULK, unlike `resolveUrl`, because a page is the one place every
|
|
811
|
+
* reference is known at once, and a host that presigns can mint them all in
|
|
812
|
+
* one round trip. Answer a `Map` or a plain record from reference to URL;
|
|
813
|
+
* leave a reference out, or answer `null`, for one this host cannot resolve.
|
|
814
|
+
* Each answer passes the URL gate like any resolver's.
|
|
815
|
+
*
|
|
816
|
+
* Optional. Unset, or for a reference it leaves out, each reference is asked
|
|
817
|
+
* of `resolveUrl`; neither answering leaves that picture broken and the page
|
|
818
|
+
* shown. A call that rejects or takes longer than ten seconds is read as no
|
|
819
|
+
* answer, so a page is never held back by it for good.
|
|
820
|
+
*/
|
|
821
|
+
resolveUrls?: ResolveUrls;
|
|
335
822
|
resolveShareUrl?: ResolveShareUrl;
|
|
336
823
|
proseImages?: ProseImages;
|
|
337
824
|
/**
|
|
@@ -370,7 +857,7 @@ interface ResultEnv {
|
|
|
370
857
|
*/
|
|
371
858
|
tableColumns?: number;
|
|
372
859
|
}
|
|
373
|
-
declare function ResultEnvProvider({ resolveUrl, resolveShareUrl, proseImages, tableColumns, saveFiles, downloads, children, }: ResultEnv & {
|
|
860
|
+
declare function ResultEnvProvider({ resolveUrl, resolveUrls, resolveShareUrl, proseImages, tableColumns, saveFiles, downloads, children, }: ResultEnv & {
|
|
374
861
|
children: ReactNode;
|
|
375
862
|
}): react.JSX.Element;
|
|
376
863
|
/**
|
|
@@ -413,6 +900,23 @@ declare function useProseImages(): ProseImages;
|
|
|
413
900
|
* match a host's design system. It is a renderer that cannot be improved without
|
|
414
901
|
* shipping the engine. If a plain-text form is wanted, it is a COPY FORMAT and
|
|
415
902
|
* belongs behind a copy control, not beside the view that reads the standard.
|
|
903
|
+
*
|
|
904
|
+
* ## Which views a reader is offered
|
|
905
|
+
*
|
|
906
|
+
* The JSON view is a builder's tool. In `studio` both views are offered; in
|
|
907
|
+
* `app` only the result is, so a person reading a method app's answer is not
|
|
908
|
+
* handed a switch to a receipt they have no use for. `views` overrides either
|
|
909
|
+
* default, and the switch is drawn only when it offers more than one view.
|
|
910
|
+
*
|
|
911
|
+
* ## A result that is one page
|
|
912
|
+
*
|
|
913
|
+
* A result whose node IS `native.Html`, or a concept refining it (read off the
|
|
914
|
+
* descriptor's `concept_ref` and `refines`, never off the value), is one page,
|
|
915
|
+
* and the panel shows it as that page: one row holding the title (none in
|
|
916
|
+
* `app`, where the page carries its own), "Download
|
|
917
|
+
* PDF" as the primary control and one HTML download, then the page edge to
|
|
918
|
+
* edge with no box around it. The page's own control row and the header's
|
|
919
|
+
* download saved the same file, so they are one control here.
|
|
416
920
|
*/
|
|
417
921
|
type StuffViewerView = 'rendered' | 'json';
|
|
418
922
|
interface StuffViewerProps {
|
|
@@ -438,7 +942,18 @@ interface StuffViewerProps {
|
|
|
438
942
|
* is named after the thing they were reading rather than `output.json`.
|
|
439
943
|
*/
|
|
440
944
|
name?: string;
|
|
441
|
-
/**
|
|
945
|
+
/**
|
|
946
|
+
* Which views the reader may switch between, in the switch's order. Unset,
|
|
947
|
+
* it follows the presentation: both in `studio`, the rendered result alone in
|
|
948
|
+
* `app`, where the JSON receipt is a builder's tool. The switch is drawn only
|
|
949
|
+
* when more than one view is offered; an empty list counts as the rendered
|
|
950
|
+
* result alone.
|
|
951
|
+
*/
|
|
952
|
+
views?: readonly StuffViewerView[];
|
|
953
|
+
/**
|
|
954
|
+
* Which view opens first. Rendered, unless a host has a reason. A view not
|
|
955
|
+
* among `views` opens the first one offered instead.
|
|
956
|
+
*/
|
|
442
957
|
defaultView?: StuffViewerView;
|
|
443
958
|
/**
|
|
444
959
|
* Names the saved files, whichever control saved them: the base name names
|
|
@@ -507,17 +1022,189 @@ interface HtmlPreviewProps {
|
|
|
507
1022
|
* 480px showed three paragraphs of one with a scrollbar down the side while
|
|
508
1023
|
* the panel around it sat empty — the reader scrolls twice to read once. The
|
|
509
1024
|
* frame still sizes itself to its CONTENT, so a short snippet stays short;
|
|
510
|
-
* this only says how far it may grow before scrolling instead.
|
|
1025
|
+
* this only says how far it may grow before scrolling instead. A whole
|
|
1026
|
+
* document has no limit unless one is passed: it is a page, and is read whole.
|
|
511
1027
|
*/
|
|
512
1028
|
maxHeight?: number;
|
|
513
1029
|
/**
|
|
514
|
-
* The frame's `img-src`. Defaults to `data: https:` — a method's HTML
|
|
515
|
-
* embeds the images that method produced. Pass `"data:"` to block
|
|
516
|
-
* images entirely, or name an origin to allow only your own storage.
|
|
1030
|
+
* The frame's `img-src`. Defaults to `'self' data: https:` — a method's HTML
|
|
1031
|
+
* result embeds the images that method produced. Pass `"data:"` to block
|
|
1032
|
+
* remote images entirely, or name an origin to allow only your own storage.
|
|
1033
|
+
*/
|
|
1034
|
+
imgSrc?: string;
|
|
1035
|
+
/**
|
|
1036
|
+
* The frame's `font-src`. Follows `imgSrc` when unset: a page's fonts come
|
|
1037
|
+
* from where its images do, the host's own origin or a font service, and a
|
|
1038
|
+
* blocked one silently changes the face the page was designed in.
|
|
517
1039
|
*/
|
|
1040
|
+
fontSrc?: string;
|
|
1041
|
+
/**
|
|
1042
|
+
* Draw the "Download PDF" control above the page. Follows the result
|
|
1043
|
+
* environment's `downloads.pdf` when unset, which draws it. The result view
|
|
1044
|
+
* passes `false` and draws its own in the page's control row.
|
|
1045
|
+
*/
|
|
1046
|
+
downloadPdf?: boolean;
|
|
1047
|
+
/** The PDF's file name, without the extension. `page` when unset. */
|
|
1048
|
+
pdfFileName?: string;
|
|
1049
|
+
/**
|
|
1050
|
+
* Draw a whole document with no border and no rounding, edge to edge with
|
|
1051
|
+
* its container: for a page that IS the view (a result that is one page),
|
|
1052
|
+
* where a box around it is chrome around chrome. A fragment keeps its themed
|
|
1053
|
+
* box either way, since that box is its padding.
|
|
1054
|
+
*/
|
|
1055
|
+
bare?: boolean;
|
|
1056
|
+
/**
|
|
1057
|
+
* Let a whole document's frame shrink to the height its container leaves it
|
|
1058
|
+
* and scroll inside itself, when that container is a flex column of bounded
|
|
1059
|
+
* height (a host's scrolling panel, `flex min-h-0 flex-col overflow-y-auto`).
|
|
1060
|
+
* The page is then the one scroll container under the pointer, so the first
|
|
1061
|
+
* wheel gesture over it scrolls it. In any other container the frame stays
|
|
1062
|
+
* as tall as the page. The result view passes it for a result that is one
|
|
1063
|
+
* page; a fragment ignores it.
|
|
1064
|
+
*/
|
|
1065
|
+
fill?: boolean;
|
|
1066
|
+
}
|
|
1067
|
+
declare function HtmlPreview({ content, maxHeight, imgSrc, fontSrc, downloadPdf, pdfFileName, bare, fill, }: HtmlPreviewProps): react.JSX.Element;
|
|
1068
|
+
|
|
1069
|
+
/**
|
|
1070
|
+
* A page's print stylesheet, applied on screen: what the PDF copy needs before
|
|
1071
|
+
* it is laid out and rasterised, since a browser offers no way to lay a frame
|
|
1072
|
+
* out as `print` media short of printing it.
|
|
1073
|
+
*
|
|
1074
|
+
* ## Promoting the print rules
|
|
1075
|
+
*
|
|
1076
|
+
* Every media query in the copy's stylesheets is rewritten to say what it would
|
|
1077
|
+
* say on paper: a query on `print` becomes one on `all`, a query on `screen` (or
|
|
1078
|
+
* any other media type) becomes `not all`, and `not` flips both, so `@media
|
|
1079
|
+
* print { … }` now applies and `@media screen { … }` no longer does, in the
|
|
1080
|
+
* order the author wrote them. A query with no media type (`@media (min-width:
|
|
1081
|
+
* 600px)`) is left alone: it is about the page's size, which the copy is laid
|
|
1082
|
+
* out at. A `<style media="print">` is rewritten the same way.
|
|
1083
|
+
*
|
|
1084
|
+
* Rewriting the queries in place, rather than unwrapping the rules out of their
|
|
1085
|
+
* blocks, keeps the cascade exactly as written: a print rule still comes after
|
|
1086
|
+
* the screen rules it overrides, and nested blocks keep their nesting.
|
|
1087
|
+
*
|
|
1088
|
+
* ## The page box
|
|
1089
|
+
*
|
|
1090
|
+
* `@page` says the sheet's size and margins. A rule with no selector is read,
|
|
1091
|
+
* the last one winning per property, as in the cascade; `:first`, `:left` and
|
|
1092
|
+
* named pages are not. A size is a keyword (`A4`, `letter`, optionally with
|
|
1093
|
+
* `landscape`) or two lengths; a margin is a length. What a page does not say is
|
|
1094
|
+
* A4 portrait with 1 cm margins on every side — what Chrome's own print uses
|
|
1095
|
+
* when a page names none, so a template laid out against Chrome's print lands
|
|
1096
|
+
* the same way.
|
|
1097
|
+
*/
|
|
1098
|
+
/** A page's size and margins, in CSS pixels (96 to the inch). */
|
|
1099
|
+
interface PageBox {
|
|
1100
|
+
width: number;
|
|
1101
|
+
height: number;
|
|
1102
|
+
margin: {
|
|
1103
|
+
top: number;
|
|
1104
|
+
right: number;
|
|
1105
|
+
bottom: number;
|
|
1106
|
+
left: number;
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
/**
|
|
1111
|
+
* Make a PDF of an HTML page in the browser, with no server and no dialog: lay
|
|
1112
|
+
* a script-free copy of the page out as it would print, rasterise it, cut the
|
|
1113
|
+
* raster into pages at block boundaries, and wrap the pages in a PDF.
|
|
1114
|
+
*
|
|
1115
|
+
* ## The copy
|
|
1116
|
+
*
|
|
1117
|
+
* The page is laid out in a hidden frame of its own, never the preview on
|
|
1118
|
+
* screen, which carries the host's theme and is sized to the panel. The frame
|
|
1119
|
+
* document is the preview's (`html-frame.ts`), under the same policy: the same
|
|
1120
|
+
* `img-src` and `font-src`, no scripts. It is sandboxed `allow-same-origin` and
|
|
1121
|
+
* nothing else, so the host can read its layout and computed style, and the
|
|
1122
|
+
* markup still runs nothing: same-origin is dangerous only together with
|
|
1123
|
+
* scripts.
|
|
1124
|
+
*
|
|
1125
|
+
* Once loaded, every image is made eager and awaited and every face the page
|
|
1126
|
+
* declares is asked for and awaited, a face used only in print included; then
|
|
1127
|
+
* the page's print rules are promoted (`print-media.ts`), its `@page` box read,
|
|
1128
|
+
* and the frame resized to the page's drawable width and height, so the copy is
|
|
1129
|
+
* laid out as the print engine would lay it out on one sheet.
|
|
1130
|
+
*
|
|
1131
|
+
* ## The raster
|
|
1132
|
+
*
|
|
1133
|
+
* `modern-screenshot` clones the copy into an SVG `foreignObject`, inlining each
|
|
1134
|
+
* element's computed style and embedding the fonts and images it uses as data
|
|
1135
|
+
* URLs, which is what lets the browser paint it as an image. It is rendered
|
|
1136
|
+
* once for the flow, with the elements laid out `position: fixed` left out,
|
|
1137
|
+
* and once for those elements alone at one page's height, laid over every page
|
|
1138
|
+
* as a print engine repeats them. Each stretch of the flow a page draws is
|
|
1139
|
+
* painted from the SVG through its own `viewBox` at `scale` device pixels per
|
|
1140
|
+
* CSS pixel, so only one page is ever held as pixels, and text is rasterised
|
|
1141
|
+
* at print resolution rather than scaled up.
|
|
1142
|
+
*
|
|
1143
|
+
* ## The PDF
|
|
1144
|
+
*
|
|
1145
|
+
* Each page is encoded as a JPEG and placed full-bleed on a page of the size
|
|
1146
|
+
* the page box says, by `jspdf`. Both libraries are loaded with `import()` on
|
|
1147
|
+
* first use, so a host that never makes a PDF ships neither.
|
|
1148
|
+
*/
|
|
1149
|
+
interface HtmlPdfOptions {
|
|
1150
|
+
/** The copy's `img-src`, as `HtmlPreview`'s. */
|
|
518
1151
|
imgSrc?: string;
|
|
1152
|
+
/** The copy's `font-src`, as `HtmlPreview`'s. Follows `imgSrc` when unset. */
|
|
1153
|
+
fontSrc?: string;
|
|
1154
|
+
/** The typeface a fragment is laid out in, when its own CSS names none. */
|
|
1155
|
+
fontFamily?: string;
|
|
1156
|
+
/** The PDF's document title, in its metadata. */
|
|
1157
|
+
title?: string;
|
|
1158
|
+
/**
|
|
1159
|
+
* Device pixels per CSS pixel of the raster. 3 (288 dpi) when unset; 2 is
|
|
1160
|
+
* the least that prints crisp.
|
|
1161
|
+
*/
|
|
1162
|
+
scale?: number;
|
|
1163
|
+
/** JPEG quality of each page, from 0 to 1. 0.92 when unset. */
|
|
1164
|
+
quality?: number;
|
|
519
1165
|
}
|
|
520
|
-
|
|
1166
|
+
/** What `renderHtmlPdf` produced, for a caller that wants more than the bytes. */
|
|
1167
|
+
interface HtmlPdf {
|
|
1168
|
+
blob: Blob;
|
|
1169
|
+
pageCount: number;
|
|
1170
|
+
/** The page box the PDF was laid out on, in CSS pixels. */
|
|
1171
|
+
page: PageBox;
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Render `content` to a PDF. Rejects when the copy cannot be built or painted,
|
|
1175
|
+
* or when the libraries that paint and assemble it cannot be loaded.
|
|
1176
|
+
*/
|
|
1177
|
+
declare function renderHtmlPdf(content: HtmlContentView, { imgSrc, fontSrc, fontFamily, title, scale, quality, }?: HtmlPdfOptions): Promise<HtmlPdf>;
|
|
1178
|
+
|
|
1179
|
+
interface PdfDownloadButtonProps {
|
|
1180
|
+
/** The page to make a PDF of. */
|
|
1181
|
+
content: HtmlContentView;
|
|
1182
|
+
/** The PDF's file name, without the extension. `page` when unset or empty. */
|
|
1183
|
+
fileName?: string;
|
|
1184
|
+
/** Where the page sits in the result, for the save plan (`path`). */
|
|
1185
|
+
path?: string;
|
|
1186
|
+
/** The PDF copy's `img-src`, as `HtmlPreview`'s. */
|
|
1187
|
+
imgSrc?: string;
|
|
1188
|
+
/** The PDF copy's `font-src`, as `HtmlPreview`'s. */
|
|
1189
|
+
fontSrc?: string;
|
|
1190
|
+
/** Draw it as the row's primary control: filled rather than outlined. */
|
|
1191
|
+
primary?: boolean;
|
|
1192
|
+
className?: string;
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1195
|
+
* "Download PDF" for an HTML page: one click, and the browser saves
|
|
1196
|
+
* `<fileName>.pdf`. The PDF is made in the browser (`renderHtmlPdf`) from a
|
|
1197
|
+
* script-free copy of the page laid out with its print rules, and handed to the
|
|
1198
|
+
* result environment's `saveFiles` like every other download, so a host that
|
|
1199
|
+
* delivers files its own way delivers this one too. The stored pictures the
|
|
1200
|
+
* page names are resolved through the environment's resolvers first, as its
|
|
1201
|
+
* preview's are.
|
|
1202
|
+
*
|
|
1203
|
+
* Busy while the PDF is made, which takes a moment for the fonts and the
|
|
1204
|
+
* raster; a PDF that could not be made or saved turns the control into a
|
|
1205
|
+
* warning, described to assistive technology, until the reader tries again.
|
|
1206
|
+
*/
|
|
1207
|
+
declare function PdfDownloadButton({ content, fileName, path, imgSrc, fontSrc, primary, className, }: PdfDownloadButtonProps): react.JSX.Element;
|
|
521
1208
|
|
|
522
1209
|
interface MarkdownProps {
|
|
523
1210
|
text: string;
|
|
@@ -577,155 +1264,14 @@ interface OptionalToggleProps {
|
|
|
577
1264
|
* The disclosure for empty optional entries. Optional inputs stay hidden until
|
|
578
1265
|
* the user asks for them - required (and already-filled) entries always show, so
|
|
579
1266
|
* the form opens at its simplest and grows only on demand. Used at the top level
|
|
580
|
-
* and inside every structured concept, at any nesting depth.
|
|
1267
|
+
* and inside every structured concept, at any nesting depth. An optional
|
|
1268
|
+
* structure folds behind it too, and opens and closes with it: see
|
|
1269
|
+
* `openOptionalStructures` / `closeOptionalStructures` in the core.
|
|
581
1270
|
*/
|
|
582
1271
|
declare function OptionalToggle({ count, expanded, onToggle, noun }: OptionalToggleProps): react.JSX.Element;
|
|
583
1272
|
|
|
584
1273
|
declare const fieldControlClass: string;
|
|
585
1274
|
|
|
586
|
-
interface FieldStrings {
|
|
587
|
-
/** Badge on an optional field's header row. */
|
|
588
|
-
optionalBadge: string;
|
|
589
|
-
selectPlaceholder: string;
|
|
590
|
-
typeValuePlaceholder: string;
|
|
591
|
-
writeHerePlaceholder: string;
|
|
592
|
-
/** Helper under the raw-JSON escape hatch. */
|
|
593
|
-
jsonHint: string;
|
|
594
|
-
decrease: string;
|
|
595
|
-
increase: string;
|
|
596
|
-
noItemsYet: string;
|
|
597
|
-
addItem: string;
|
|
598
|
-
removeItemAria: (index: number) => string;
|
|
599
|
-
itemsCount: (count: number) => string;
|
|
600
|
-
/** The same badge for a list the method gave an exact count (`Concept[N]`). */
|
|
601
|
-
itemsCountOf: (count: number, total: number) => string;
|
|
602
|
-
/** What a record's table cell says when none of its fields can name it. */
|
|
603
|
-
fieldsCount: (count: number) => string;
|
|
604
|
-
uploading: string;
|
|
605
|
-
dropToUpload: string;
|
|
606
|
-
dropOrBrowse: string;
|
|
607
|
-
pasteUrlInstead: string;
|
|
608
|
-
/**
|
|
609
|
-
* What a file field says in place of its dropzone when its host has no way to
|
|
610
|
-
* store a file, so a link is the only way in. The field's format hint follows
|
|
611
|
-
* it, because the link still has to point at a file the runtime can read.
|
|
612
|
-
*/
|
|
613
|
-
uploadUnavailable: string;
|
|
614
|
-
/**
|
|
615
|
-
* The placeholder in the URL a person may paste instead of uploading. It names
|
|
616
|
-
* the web's own scheme only: a host whose runner also takes its own storage
|
|
617
|
-
* references can say so by overriding it, and no default shows an end user a
|
|
618
|
-
* platform's storage scheme.
|
|
619
|
-
*/
|
|
620
|
-
urlPlaceholder: string;
|
|
621
|
-
/**
|
|
622
|
-
* The accessible name of that URL input, given the field's label as the form
|
|
623
|
-
* shows it. The field's label is bound to the file input, so without this the
|
|
624
|
-
* placeholder was the only thing a screen reader announced here.
|
|
625
|
-
*
|
|
626
|
-
* The label is EMPTY inside a list row: the list drops each row's label, and
|
|
627
|
-
* the row number it shows instead is a bare glyph tied to no control. So every
|
|
628
|
-
* row's input currently carries the same generic name, and giving rows
|
|
629
|
-
* distinct names is a separate piece of work on the list control.
|
|
630
|
-
*/
|
|
631
|
-
fileUrlAria: (label: string) => string;
|
|
632
|
-
/**
|
|
633
|
-
* The title of an attached file's card when the value carries no filename.
|
|
634
|
-
* True of a pasted link as well as of an upload, which "Uploaded file" was not.
|
|
635
|
-
*/
|
|
636
|
-
uploadedFile: string;
|
|
637
|
-
/**
|
|
638
|
-
* What names a file carried INSIDE its own URL (a `data:` URL), where a
|
|
639
|
-
* reference would otherwise be printed: its format and its decoded size. The
|
|
640
|
-
* string itself is forty thousand characters of base64 and says nothing.
|
|
641
|
-
*/
|
|
642
|
-
encodedFileSummary: (format: string, bytes: number) => string;
|
|
643
|
-
preview: string;
|
|
644
|
-
removeFileAria: string;
|
|
645
|
-
previewUnavailablePdf: string;
|
|
646
|
-
/**
|
|
647
|
-
* What a result slot the run resolved as an absence SHOWS — a dash, not a
|
|
648
|
-
* sentence. Beside forty values in a grid, "not provided" repeated is louder
|
|
649
|
-
* than the data; a dash reads as the blank it is.
|
|
650
|
-
*/
|
|
651
|
-
resultAbsent: string;
|
|
652
|
-
/**
|
|
653
|
-
* What that dash SAYS, to a screen reader.
|
|
654
|
-
*
|
|
655
|
-
* The two are separate because a glyph and a sentence are answers to different
|
|
656
|
-
* questions. A table column of hyphens read aloud is "hyphen, hyphen, hyphen",
|
|
657
|
-
* which is noise rather than information — so the dash is `aria-hidden` and
|
|
658
|
-
* this rides beside it, visually hidden.
|
|
659
|
-
*/
|
|
660
|
-
resultAbsentDescription: string;
|
|
661
|
-
/**
|
|
662
|
-
* Shown above the raw value when a result cannot be laid out because the
|
|
663
|
-
* artifacts describing it are absent. It names the CAUSE — the descriptor,
|
|
664
|
-
* not the data — because the value is right there underneath and a reader
|
|
665
|
-
* who is not told why will assume the view is broken.
|
|
666
|
-
*/
|
|
667
|
-
resultUndescribed: string;
|
|
668
|
-
/** The control that opens a result table row to show the rest of the record. */
|
|
669
|
-
toggleRowDetails: (index: number) => string;
|
|
670
|
-
/** The (visually hidden) header of the column those controls sit in. */
|
|
671
|
-
rowDetailsColumn: string;
|
|
672
|
-
/** The control that puts a file's whole URL on the clipboard. */
|
|
673
|
-
copyUrl: string;
|
|
674
|
-
/** The copy control beside any text value, markdown or not. */
|
|
675
|
-
copyText: string;
|
|
676
|
-
/** The download control's label. */
|
|
677
|
-
download: string;
|
|
678
|
-
/** Announced while the files are being fetched. */
|
|
679
|
-
downloading: string;
|
|
680
|
-
/**
|
|
681
|
-
* What the whole-result download says when some files did not arrive: the
|
|
682
|
-
* ones no URL could be found for, and the ones the delivery reported. Takes
|
|
683
|
-
* the names the files would have been saved under.
|
|
684
|
-
*/
|
|
685
|
-
downloadIncomplete: (names: readonly string[]) => string;
|
|
686
|
-
/**
|
|
687
|
-
* The button beside each file that saves that one file, given the name it
|
|
688
|
-
* saves under: a gallery of buttons all called "Download" would leave a
|
|
689
|
-
* screen-reader user unable to tell which file each one saves.
|
|
690
|
-
*/
|
|
691
|
-
downloadFile: (name: string) => string;
|
|
692
|
-
/** What that button says when the file did not arrive. */
|
|
693
|
-
downloadFileFailed: string;
|
|
694
|
-
/** The result panel's two views, and the control that copies the payload. */
|
|
695
|
-
viewRendered: string;
|
|
696
|
-
viewJson: string;
|
|
697
|
-
copyJson: string;
|
|
698
|
-
resultViewGroup: string;
|
|
699
|
-
yes: string;
|
|
700
|
-
no: string;
|
|
701
|
-
/**
|
|
702
|
-
* Shown when a picked file is not a format this slot accepts. Takes the
|
|
703
|
-
* slot's own formats as the hint names them (`PNG, JPG`), after any narrowing
|
|
704
|
-
* a host applied, so the message names what WOULD have worked - "not
|
|
705
|
-
* accepted" on its own leaves the user guessing at the list they just failed.
|
|
706
|
-
*/
|
|
707
|
-
unsupportedFileType: (formats: string) => string;
|
|
708
|
-
/** The optional-entries disclosure ("field" inside a concept, "input" at top level). */
|
|
709
|
-
hideOptionalFields: string;
|
|
710
|
-
hideOptionalInputs: string;
|
|
711
|
-
optionalFieldsCount: (count: number) => string;
|
|
712
|
-
optionalInputsCount: (count: number) => string;
|
|
713
|
-
}
|
|
714
|
-
/** The English strings, which every control falls back to. Kept under this name for existing hosts. */
|
|
715
|
-
declare const DEFAULT_FIELD_STRINGS: FieldStrings;
|
|
716
|
-
/**
|
|
717
|
-
* Set the strings every control below speaks. `locale` picks a language pack by its code (`fr`,
|
|
718
|
-
* `fr-FR`, `en-GB`…; an unknown one falls back to English), and `strings` overrides single keys
|
|
719
|
-
* on top of it. Both are optional: with neither, the controls speak English.
|
|
720
|
-
*/
|
|
721
|
-
declare function FieldStringsProvider({ locale, strings, children, }: {
|
|
722
|
-
locale?: string;
|
|
723
|
-
strings?: Partial<FieldStrings>;
|
|
724
|
-
children: ReactNode;
|
|
725
|
-
}): react.JSX.Element;
|
|
726
|
-
/** The strings the nearest provider supplies (English defaults when none does). */
|
|
727
|
-
declare function useFieldStrings(): FieldStrings;
|
|
728
|
-
|
|
729
1275
|
/**
|
|
730
1276
|
* The controls' strings in English: the package's defaults, and the pack every unknown or missing
|
|
731
1277
|
* locale falls back to (see `fieldStringsFor` in `./index.ts`).
|
|
@@ -785,43 +1331,4 @@ declare function FieldDomIdProvider({ prefix, children, }: {
|
|
|
785
1331
|
*/
|
|
786
1332
|
declare function useFieldDomId(path: string): string;
|
|
787
1333
|
|
|
788
|
-
|
|
789
|
-
* How a field's LABEL chrome, and the enum values a person reads, should read.
|
|
790
|
-
* What a control stores never changes.
|
|
791
|
-
*
|
|
792
|
-
* - `studio` (default): the field's name verbatim in mono, with its concept
|
|
793
|
-
* pill. The name IS the identifier a builder writes in the `.mthds` file, so
|
|
794
|
-
* showing it exactly - and typed - is the point. An enum value is shown as its
|
|
795
|
-
* code for the same reason: it has to match the bundle and the JSON the
|
|
796
|
-
* builder reads next.
|
|
797
|
-
* - `app`: the field's name as a human question in sans, no concept pill. Inside
|
|
798
|
-
* a method app, `full_name` and `native.Text` are implementation detail; the
|
|
799
|
-
* person filling the form has never seen the method's source. An enum value
|
|
800
|
-
* follows the label and reads as words.
|
|
801
|
-
*
|
|
802
|
-
* An enum value is the only value the two presentations show differently. A
|
|
803
|
-
* number, a text, a date and every other value render the same in both.
|
|
804
|
-
*
|
|
805
|
-
* Deliberately a context rather than a prop or a `FieldEnv` flag: only the
|
|
806
|
-
* components that own label chrome or an enum value's wording read it -
|
|
807
|
-
* `FieldShell`, `ObjectField`, `BooleanField`, `ListField`, `EnumField` and the
|
|
808
|
-
* result view - plus the file control, whose card hides a storage reference in
|
|
809
|
-
* `app`, and threading a prop would have meant editing every field control and
|
|
810
|
-
* both recursive containers.
|
|
811
|
-
*/
|
|
812
|
-
type FieldPresentation = 'studio' | 'app';
|
|
813
|
-
declare function FieldPresentationProvider({ presentation, children, }: {
|
|
814
|
-
presentation: FieldPresentation;
|
|
815
|
-
children: ReactNode;
|
|
816
|
-
}): react.JSX.Element;
|
|
817
|
-
declare function useFieldPresentation(): FieldPresentation;
|
|
818
|
-
/**
|
|
819
|
-
* `full_name` → `Full name`. Only the first word is capitalised: these are
|
|
820
|
-
* labels, not titles, and Title Case on every word reads as a form from 2009.
|
|
821
|
-
* Takes IDENTIFIERS only - snake_case or kebab-case names, where a separator
|
|
822
|
-
* is structure. An authored `title` never goes through here: it is already
|
|
823
|
-
* human-facing, and its hyphens are spelling (`E-mail address`), not structure.
|
|
824
|
-
*/
|
|
825
|
-
declare function humanizeFieldName(name: string): string;
|
|
826
|
-
|
|
827
|
-
export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, type DownloadDisplay, EN_FIELD_STRINGS, EnumField, FIELD_STRINGS_BY_LOCALE, FR_FIELD_STRINGS, FieldDomIdProvider, FieldEnv, type FieldPresentation, FieldPresentationProvider, FieldShell, type FieldStrings, FieldStringsProvider, type FileValue, HtmlPreview, type HtmlPreviewProps, ImageField, JsonView, ListField, Markdown, type MarkdownProps, NumberField, ObjectField, OptionalToggle, ProseField, type ProseImages, type ResolveShareUrl, type ResolveUrl, ResultEnvProvider, ResultField, type ResultFieldProps, SUPPORTED_LOCALES, StuffViewer, type StuffViewerProps, type StuffViewerView, TextField, UnknownField, fieldControlClass, fieldStringsFor, humanizeFieldName, saveInBrowser, useFieldDomId, useFieldPresentation, useFieldStrings, useProseImages, useResolveShareUrl, useResolveUrl, useResolvedUrl };
|
|
1334
|
+
export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, type DownloadDisplay, EN_FIELD_STRINGS, EnumField, FIELD_STRINGS_BY_LOCALE, FR_FIELD_STRINGS, FieldDomIdProvider, FieldEnv, type FieldPresentation, FieldPresentationProvider, FieldShell, type FieldStrings, FieldStringsProvider, type FileValue, type HtmlPdf, type HtmlPdfOptions, HtmlPreview, type HtmlPreviewProps, ImageField, JsonView, ListField, Markdown, type MarkdownProps, type MarkupResolvers, MethodForm, type MethodFormController, type MethodFormProps, type MethodFormState, type MethodFormUploadContext, type MethodFormValues, NumberField, ObjectField, OptionalToggle, PdfDownloadButton, type PdfDownloadButtonProps, ProseField, type ProseImages, type ResolveShareUrl, type ResolveUrl, type ResolveUrls, ResultEnvProvider, ResultField, type ResultFieldProps, SUPPORTED_LOCALES, StuffViewer, type StuffViewerProps, type StuffViewerView, TextField, UnknownField, type UploadedFile, type UseMethodFormOptions, fieldControlClass, fieldStringsFor, humanizeFieldName, renderHtmlPdf, resolveHtmlStorageUrls, saveInBrowser, useFieldDomId, useFieldPresentation, useFieldStrings, useMethodForm, useProseImages, useResolveShareUrl, useResolveUrl, useResolvedUrl };
|