@dmitryvim/form-builder 0.3.0 → 0.3.2
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 +32 -6
- package/dist/browser/formbuilder.min.js +146 -116
- package/dist/browser/formbuilder.v0.3.2.min.js +1441 -0
- package/dist/cjs/index.cjs +398 -156
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +390 -154
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +146 -116
- package/dist/types/components/file/library.d.ts +40 -16
- package/dist/types/components/file/upload.d.ts +111 -2
- package/dist/types/utils/styles.d.ts +24 -1
- package/package.json +1 -1
- package/dist/browser/formbuilder.v0.3.0.min.js +0 -1411
|
@@ -1,27 +1,51 @@
|
|
|
1
1
|
import type { FormBuilderInstance } from "../../instance/FormBuilderInstance.js";
|
|
2
2
|
import type { State } from "../../types/state.js";
|
|
3
3
|
import type { FileElement, FilesElement } from "../../types/index.js";
|
|
4
|
+
import type { BatchCoordinator } from "./upload.js";
|
|
5
|
+
export interface HandleLibraryPickMultiOptions {
|
|
6
|
+
state: State;
|
|
7
|
+
element: FileElement | FilesElement;
|
|
8
|
+
/** The `[data-files-wrapper]` element — used as the error-message target. */
|
|
9
|
+
wrapper: HTMLElement;
|
|
10
|
+
/** Bracket-notation field path (e.g. "slides[2].image"). */
|
|
11
|
+
fieldPath: string;
|
|
12
|
+
/** The mutable live array of current resource IDs (mutated in-place). */
|
|
13
|
+
resourceIds: string[];
|
|
14
|
+
/** Maximum files allowed (Infinity = no limit). */
|
|
15
|
+
maxCount: number;
|
|
16
|
+
/** Re-render callback (same role as in setupFilesPickerHandler). */
|
|
17
|
+
updateCallback: () => void;
|
|
18
|
+
instance: FormBuilderInstance;
|
|
19
|
+
/**
|
|
20
|
+
* Coordinator shared with the upload handlers. Used to read the true
|
|
21
|
+
* occupied count (committed + in-flight), commit picked rids instantly,
|
|
22
|
+
* and detect whether peer batches are still uploading so we can avoid a
|
|
23
|
+
* full rebuild that would wipe their in-flight tiles.
|
|
24
|
+
*/
|
|
25
|
+
coordinator: BatchCoordinator;
|
|
26
|
+
/** The grid container (`.files-list`) where picked tiles are appended when peer batches are in flight. */
|
|
27
|
+
list: HTMLElement;
|
|
28
|
+
/** Builds the per-rid preview tile (same callback used by uploadBatch's in-place swap). */
|
|
29
|
+
buildSuccessTile: (rid: string) => HTMLElement;
|
|
30
|
+
}
|
|
4
31
|
/**
|
|
5
32
|
* Handle a library pick action for a MULTI-file field.
|
|
6
33
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
34
|
+
* Goes through the same coordinator queue as uploadBatch — `beginBatch`
|
|
35
|
+
* claims an ordinal at the moment of click, `setResults` stages the picked
|
|
36
|
+
* rids at that ordinal, and `end` releases the reservation. Library tiles
|
|
37
|
+
* are appended to the DOM right away so the user sees them, but their rids
|
|
38
|
+
* only land in resourceIds once every earlier-started batch has also
|
|
39
|
+
* committed. This is what keeps the final form data in user-selection order
|
|
40
|
+
* when a library pick is interleaved with an in-flight upload batch — the
|
|
41
|
+
* upload's ordinal drains first regardless of which one settled later.
|
|
14
42
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* @param resourceIds The mutable live array of current resource IDs (mutated in-place)
|
|
20
|
-
* @param maxCount Maximum files allowed (Infinity = no limit)
|
|
21
|
-
* @param updateCallback Re-render callback (same pattern as upload.ts)
|
|
22
|
-
* @param instance FormBuilderInstance (for onChange events)
|
|
43
|
+
* `getOccupiedCount` (committed + in-flight) gates both the pre-picker
|
|
44
|
+
* `remainingSlots` and the post-picker `freshRemaining` so a host UI can't
|
|
45
|
+
* accept more files than maxCount even with a concurrent upload reserving
|
|
46
|
+
* slots invisibly.
|
|
23
47
|
*/
|
|
24
|
-
export declare function handleLibraryPickMulti(
|
|
48
|
+
export declare function handleLibraryPickMulti(opts: HandleLibraryPickMultiOptions): Promise<void>;
|
|
25
49
|
/**
|
|
26
50
|
* Handle a library pick action for a SINGLE-file field.
|
|
27
51
|
*
|
|
@@ -31,5 +31,114 @@ export interface UploadBatchFailure {
|
|
|
31
31
|
file: File;
|
|
32
32
|
error: Error;
|
|
33
33
|
}
|
|
34
|
-
export
|
|
35
|
-
|
|
34
|
+
export interface UploadBatchOptions {
|
|
35
|
+
/** Files that have already passed extension / mime / size / count filtering. */
|
|
36
|
+
accepted: File[];
|
|
37
|
+
/** Container that hosts the tile grid (`.files-list`); receives uploading tiles. */
|
|
38
|
+
listEl: HTMLElement | null;
|
|
39
|
+
state: State;
|
|
40
|
+
/**
|
|
41
|
+
* Whether to hide the add-tile while this batch is in flight. Computed by
|
|
42
|
+
* the caller from `coordinator.getOccupiedCount() >= maxCount` *after*
|
|
43
|
+
* reserving slots, so concurrent batches that together saturate maxCount
|
|
44
|
+
* still hide the trigger.
|
|
45
|
+
*/
|
|
46
|
+
shouldHideAddTile: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Builds the real preview tile to swap in-place when each upload resolves.
|
|
49
|
+
* When omitted, the placeholder is simply removed and the file only becomes
|
|
50
|
+
* visible after the caller re-renders the grid — used by paths that don't
|
|
51
|
+
* own a per-rid render strategy.
|
|
52
|
+
*/
|
|
53
|
+
buildSuccessTile?: (rid: string) => HTMLElement;
|
|
54
|
+
/**
|
|
55
|
+
* Runs once after add-tile visibility is settled but before any upload
|
|
56
|
+
* promise starts. Hook for the caller to dispose of empty placeholders and
|
|
57
|
+
* the placeholder ResizeObserver — they would otherwise compete with the
|
|
58
|
+
* uploading tiles and the observer would immediately re-add removed slots.
|
|
59
|
+
*/
|
|
60
|
+
prepareForUpload?: () => void;
|
|
61
|
+
}
|
|
62
|
+
export interface UploadBatchResult {
|
|
63
|
+
failures: UploadBatchFailure[];
|
|
64
|
+
/**
|
|
65
|
+
* One slot per `accepted[i]`, filled with the uploaded resource id when its
|
|
66
|
+
* upload succeeded and `null` when it failed. Callers commit these into
|
|
67
|
+
* `resourceIds` themselves so they can filter out IDs the user removed
|
|
68
|
+
* mid-batch (see `BatchCoordinator.wasRemovedDuringBatch`).
|
|
69
|
+
*/
|
|
70
|
+
orderedIds: (string | null)[];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Owns the cross-batch invariants the handlers can't enforce on their own:
|
|
74
|
+
* tracking how many slots are reserved by in-flight uploads, preserving
|
|
75
|
+
* user-selection order across concurrent batches that settle out-of-start
|
|
76
|
+
* order, filtering out resources the user removed before a batch settles,
|
|
77
|
+
* and gating the final grid rebuild until the last concurrent batch finishes.
|
|
78
|
+
*
|
|
79
|
+
* Both the drop and picker handlers share a single coordinator instance per
|
|
80
|
+
* field so that batches started while another batch is still pending can't
|
|
81
|
+
* bypass maxCount, scramble the form-data order, or have their tiles wiped
|
|
82
|
+
* by a peer's rebuild.
|
|
83
|
+
*/
|
|
84
|
+
export interface BatchCoordinator {
|
|
85
|
+
/** Settled resource count plus in-flight reservations. */
|
|
86
|
+
getOccupiedCount: () => number;
|
|
87
|
+
/**
|
|
88
|
+
* Snapshot of every rid currently in the system: committed in resourceIds
|
|
89
|
+
* plus rids staged behind an earlier ordinal awaiting drain. Used by the
|
|
90
|
+
* library picker to dedupe against — committed-only would let a staged rid
|
|
91
|
+
* be re-picked and committed twice.
|
|
92
|
+
*/
|
|
93
|
+
getAllKnownRids: () => string[];
|
|
94
|
+
/** True if any batches are currently in flight (used to gate full rebuilds). */
|
|
95
|
+
hasInFlightBatches: () => boolean;
|
|
96
|
+
/** True if `rid` was removed during the current in-flight period. */
|
|
97
|
+
wasRemovedDuringBatch: (rid: string) => boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Reserve N slots and begin a new batch. The returned handle is the only
|
|
100
|
+
* way to stage results and release the reservation — staging through a
|
|
101
|
+
* handle keyed by batch ordinal is what guarantees start-order commits
|
|
102
|
+
* even when batches settle out of order. Both the upload path and the
|
|
103
|
+
* library picker go through this so they share the same ordering queue.
|
|
104
|
+
*/
|
|
105
|
+
beginBatch: (count: number) => BatchHandle;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Per-batch handle. `setResults` stages the batch's `orderedIds` keyed by its
|
|
109
|
+
* start ordinal; the coordinator then commits the contiguous prefix of
|
|
110
|
+
* already-settled batches into the field's resourceIds. `end` releases the
|
|
111
|
+
* reservation and reports whether no other batches remain in flight.
|
|
112
|
+
*/
|
|
113
|
+
export interface BatchHandle {
|
|
114
|
+
setResults: (orderedIds: (string | null)[]) => void;
|
|
115
|
+
end: () => {
|
|
116
|
+
wasLast: boolean;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Shared inputs for both drop and picker handlers — the only difference is
|
|
121
|
+
* which DOM event source feeds files into the batch upload.
|
|
122
|
+
*/
|
|
123
|
+
export interface MultiFileHandlerOptions {
|
|
124
|
+
resourceIds: string[];
|
|
125
|
+
state: State;
|
|
126
|
+
/** Re-renders the grid after the *last* batch settles (counter, placeholders, etc.). */
|
|
127
|
+
updateCallback: () => void;
|
|
128
|
+
constraints: FileUploadConstraints;
|
|
129
|
+
pathKey?: string;
|
|
130
|
+
instance?: FormBuilderInstance | null;
|
|
131
|
+
/** Forwarded into uploadBatch — builds the per-rid preview tile. */
|
|
132
|
+
buildSuccessTile?: (rid: string) => HTMLElement;
|
|
133
|
+
/** Forwarded into uploadBatch — runs once before any upload starts. */
|
|
134
|
+
prepareForUpload?: () => void;
|
|
135
|
+
coordinator: BatchCoordinator;
|
|
136
|
+
}
|
|
137
|
+
export interface SetupFilesDropHandlerOptions extends MultiFileHandlerOptions {
|
|
138
|
+
filesContainer: HTMLElement;
|
|
139
|
+
}
|
|
140
|
+
export declare function setupFilesDropHandler(opts: SetupFilesDropHandlerOptions): void;
|
|
141
|
+
export interface SetupFilesPickerHandlerOptions extends MultiFileHandlerOptions {
|
|
142
|
+
filesPicker: HTMLInputElement;
|
|
143
|
+
}
|
|
144
|
+
export declare function setupFilesPickerHandler(opts: SetupFilesPickerHandlerOptions): void;
|
|
@@ -22,13 +22,36 @@ export declare const BIN_ICON_SVG = "<svg width=\"14\" height=\"14\" viewBox=\"0
|
|
|
22
22
|
*
|
|
23
23
|
* - `[data-fb-slide-card]` — items inside `container.displayMode:"slides"`
|
|
24
24
|
* - `[data-fb-label-row]` — every standard field-label container
|
|
25
|
+
* - `.fb-prefill-hint` — schema-driven prefill suggestion pills
|
|
25
26
|
*
|
|
26
27
|
* Idempotent via a flag attribute on the document head.
|
|
27
28
|
*/
|
|
28
29
|
export declare function ensureThemingHooks(doc: Document): void;
|
|
29
30
|
/**
|
|
30
31
|
* Make a textarea grow vertically with its content. Starts at the height
|
|
31
|
-
* required by the current value; on each input, sets height to scrollHeight
|
|
32
|
+
* required by the current value; on each input, sets height to scrollHeight
|
|
33
|
+
* plus borders.
|
|
34
|
+
*
|
|
35
|
+
* Why add borders: `scrollHeight` includes padding but not border, and our
|
|
36
|
+
* textareas use `box-sizing: border-box`. Setting `height = scrollHeight`
|
|
37
|
+
* makes the total box (border + padding + content) equal scrollHeight, which
|
|
38
|
+
* leaves the content area short by `2 * border-width` — at compact density
|
|
39
|
+
* (8px padding, 21px line-height) that's enough to clip a single line.
|
|
40
|
+
*
|
|
41
|
+
* Deferred-layout recovery (v0.3.2):
|
|
42
|
+
* A `ResizeObserver` re-runs `resize()` whenever the textarea's content-box
|
|
43
|
+
* WIDTH changes. Two host patterns the one-shot `setTimeout(0)` below can't
|
|
44
|
+
* survive on its own:
|
|
45
|
+
* 1. Form mounted inside a still-hidden / 0-width container (e.g. inactive
|
|
46
|
+
* tab, collapsed accordion, modal that isn't open yet). At setTimeout
|
|
47
|
+
* firing time `scrollHeight === 0` → height collapses to ~borderY (~2px).
|
|
48
|
+
* Once the host reveals the container, width transitions 0 → real and we
|
|
49
|
+
* re-measure.
|
|
50
|
+
* 2. Responsive width changes (host resizes the column). Wrapped content
|
|
51
|
+
* re-flows so the textarea grows/shrinks to fit.
|
|
52
|
+
* Loop guard: `resize()` only writes the HEIGHT, so we ignore observer fires
|
|
53
|
+
* where width hasn't moved — otherwise our own height writes would feed back
|
|
54
|
+
* into the observer.
|
|
32
55
|
*/
|
|
33
56
|
export declare function applyAutoExpand(textarea: HTMLTextAreaElement): void;
|
|
34
57
|
/**
|