@stapel/cdn-react 0.2.0 → 0.3.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/CHANGELOG.md +27 -0
- package/MODULE.md +18 -0
- package/README.md +20 -0
- package/dist/default/MediaGalleryField.d.ts +30 -1
- package/dist/default/MediaGalleryField.d.ts.map +1 -1
- package/dist/default/MediaGalleryField.js +11 -0
- package/dist/default/MediaGalleryField.js.map +1 -1
- package/dist/default/index.d.ts +1 -1
- package/dist/default/index.d.ts.map +1 -1
- package/llms.txt +1 -1
- package/manifest.json +1 -1
- package/package.json +2 -2
- package/src/analytics/generated/events.json +1 -1
- package/src/default/MediaGalleryField.tsx +43 -1
- package/src/default/index.ts +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @stapel/cdn-react
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 88a8be4: `<MediaGalleryField bag={…}>` — the prop the README documented and the package did not have
|
|
8
|
+
|
|
9
|
+
The field always built its own `useUploadQueue`, so a composer beside it had
|
|
10
|
+
two queues: its own, permanently empty, and the one on screen. `bag.settled` —
|
|
11
|
+
the publish gate — then talked about photos it could not see, and
|
|
12
|
+
`images_draft` went out empty while ten tiles sat there uploading. The
|
|
13
|
+
documented wiring (`<MediaGalleryField bag={gallery} />`, in this pair's README
|
|
14
|
+
and in `@stapel/listings-react`'s) simply did not compile.
|
|
15
|
+
|
|
16
|
+
Props are now a union: `{ bag }` **or** `{ max, target, initialRefs,
|
|
17
|
+
onRefsChange }`. Passing both is a type error rather than a runtime decision
|
|
18
|
+
about which queue wins.
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
const gallery = useUploadQueue({ max: 10 });
|
|
22
|
+
<ListingComposerPage images={gallery} gallerySlot={<MediaGalleryField bag={gallery} />} … />
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`test/galleryBag.test.tsx` proves the two halves see ONE queue — a pick through
|
|
26
|
+
the tiles reaches the caller's `refs` and moves the caller's `settled` — and
|
|
27
|
+
gates the README against the props declaration, because the defect was
|
|
28
|
+
documented for a whole release and nothing in the suite could tell.
|
|
29
|
+
|
|
3
30
|
## 0.2.0
|
|
4
31
|
|
|
5
32
|
### Minor Changes
|
package/MODULE.md
CHANGED
|
@@ -98,6 +98,24 @@ off has to be able to say WHICH of the two reasons it is. `bag.refs` contains
|
|
|
98
98
|
only settled references, so it is never a promise about bytes that are not
|
|
99
99
|
there.
|
|
100
100
|
|
|
101
|
+
### …and the gallery has to be drawing the same bag
|
|
102
|
+
|
|
103
|
+
`<MediaGalleryField>` takes `bag` for exactly this reason. Its props are a
|
|
104
|
+
UNION — `{ bag }` or `{ max, target, initialRefs, onRefsChange }` — so a caller
|
|
105
|
+
cannot ask for both, and a composer's wiring is:
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
const gallery = useUploadQueue({ max: 10 });
|
|
109
|
+
<ListingComposerPage images={gallery} gallerySlot={<MediaGalleryField bag={gallery} />} … />
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Until 0.2.0 the field always built its own queue, and the two gates above were
|
|
113
|
+
then computed over a queue nobody was adding to: the composer's `settled` said
|
|
114
|
+
"wait for the photos" about photos it could not see, and `images_draft` went
|
|
115
|
+
out empty while ten tiles sat on screen. The prop was in this pair's README
|
|
116
|
+
before it was in the package — `test/galleryBag.test.tsx` now gates the README
|
|
117
|
+
against the props declaration so that cannot recur.
|
|
118
|
+
|
|
101
119
|
## Upstream notes (recorded, not worked around)
|
|
102
120
|
|
|
103
121
|
- **No public read-by-reference.** `file/exists/` is owner-scoped, so nothing in
|
package/README.md
CHANGED
|
@@ -48,6 +48,26 @@ The antd skin over the same bag is one import away:
|
|
|
48
48
|
import { MediaGalleryField, ImageUploadField } from "@stapel/cdn-react/default";
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
### Whose queue is it
|
|
52
|
+
|
|
53
|
+
`<MediaGalleryField>` either owns its queue or draws yours, and the two are
|
|
54
|
+
spelled as different prop sets so that "both" cannot be written:
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
// standalone: the field owns the queue
|
|
58
|
+
<MediaGalleryField max={10} onRefsChange={(refs) => form.set("images_draft", refs)} />
|
|
59
|
+
|
|
60
|
+
// beside a composer: the CALLER owns it, and both halves see one queue
|
|
61
|
+
const gallery = useUploadQueue({ max: 10 });
|
|
62
|
+
<ListingComposerPage images={gallery} gallerySlot={<MediaGalleryField bag={gallery} />} … />
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The second form is not a convenience. A composer reads `bag.refs` as
|
|
66
|
+
`images_draft` and `bag.settled` as its publish gate; a field that built its
|
|
67
|
+
own queue left the container with two — the composer's, permanently empty, and
|
|
68
|
+
the one on screen — so the publish gate talked about photos it could not see
|
|
69
|
+
and the listing was submitted with no images at all.
|
|
70
|
+
|
|
51
71
|
## One slot (avatar, cover)
|
|
52
72
|
|
|
53
73
|
```tsx
|
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
|
+
import type { UploadQueueBag } from "../headless/useUploadQueue.js";
|
|
2
3
|
import type { CdnRef } from "../api/types.js";
|
|
3
4
|
import type { CdnUploadTarget } from "../model/upload.js";
|
|
4
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The gallery over a queue the CALLER owns.
|
|
7
|
+
*
|
|
8
|
+
* This is the shape a composer needs and the one the field did not have. A
|
|
9
|
+
* listing composer takes `bag.refs` as `images_draft` and `bag.settled` as its
|
|
10
|
+
* publish gate, so the bag it was handed and the bag the gallery draws MUST be
|
|
11
|
+
* the same object. When the field built its own, the container had two queues:
|
|
12
|
+
* the composer's (empty, because nothing was ever added to it) and the one on
|
|
13
|
+
* screen — so the publish gate said "wait for the photos" about photos it
|
|
14
|
+
* could not see, and `images_draft` went out empty.
|
|
15
|
+
*/
|
|
16
|
+
export interface MediaGalleryFieldBagProps {
|
|
17
|
+
/** The queue to draw. Hand it the same bag the composer got. */
|
|
18
|
+
bag: UploadQueueBag;
|
|
19
|
+
max?: undefined;
|
|
20
|
+
target?: undefined;
|
|
21
|
+
initialRefs?: undefined;
|
|
22
|
+
onRefsChange?: undefined;
|
|
23
|
+
}
|
|
24
|
+
/** The gallery that owns its own queue — a field standing alone. */
|
|
25
|
+
export interface MediaGalleryFieldOwnProps {
|
|
26
|
+
bag?: undefined;
|
|
5
27
|
/** How many photos this gallery holds. The storefront's composer: 10. */
|
|
6
28
|
max: number;
|
|
7
29
|
target?: CdnUploadTarget;
|
|
@@ -10,5 +32,12 @@ export interface MediaGalleryFieldProps {
|
|
|
10
32
|
/** The list to store, in display order, on every change. */
|
|
11
33
|
onRefsChange?: (refs: readonly CdnRef[]) => void;
|
|
12
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Either the caller owns the queue (`bag`) or this field does (`max` and the
|
|
37
|
+
* options that configure one). Spelled as a union rather than as optional
|
|
38
|
+
* props so that passing both — two queues, one screen, the defect above — is a
|
|
39
|
+
* type error rather than a decision this component has to make at runtime.
|
|
40
|
+
*/
|
|
41
|
+
export type MediaGalleryFieldProps = MediaGalleryFieldBagProps | MediaGalleryFieldOwnProps;
|
|
13
42
|
export declare function MediaGalleryField(props: MediaGalleryFieldProps): ReactElement;
|
|
14
43
|
//# sourceMappingURL=MediaGalleryField.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaGalleryField.d.ts","sourceRoot":"","sources":["../../src/default/MediaGalleryField.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MediaGalleryField.d.ts","sourceRoot":"","sources":["../../src/default/MediaGalleryField.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAe,YAAY,EAAE,MAAM,OAAO,CAAC;AAIvD,OAAO,KAAK,EAAc,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAEhF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAK1D;;;;;;;;;;GAUG;AACH,MAAM,WAAW,yBAAyB;IACxC,gEAAgE;IAChE,GAAG,EAAE,cAAc,CAAC;IACpB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,YAAY,CAAC,EAAE,SAAS,CAAC;CAC1B;AAED,oEAAoE;AACpE,MAAM,WAAW,yBAAyB;IACxC,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,yEAAyE;IACzE,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,4DAA4D;IAC5D,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,IAAI,CAAC;CAClD;AAED;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAC9B,yBAAyB,GACzB,yBAAyB,CAAC;AAwL9B,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,YAAY,CAgB7E"}
|
|
@@ -12,6 +12,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
12
12
|
* The first tile is labelled as the cover, because the order IS the meaning:
|
|
13
13
|
* `Listing.images_draft` is stored in this order and the first reference is
|
|
14
14
|
* what a search result card shows.
|
|
15
|
+
*
|
|
16
|
+
* ── Whose queue is it ──────────────────────────────────────────────────────
|
|
17
|
+
*
|
|
18
|
+
* Either the caller's (`bag`) or this field's (`max`). A composer consumes
|
|
19
|
+
* `bag.refs` as `images_draft` and `bag.settled` as its publish gate, so when
|
|
20
|
+
* a composer is on the page the bag it holds and the bag drawn here must be
|
|
21
|
+
* ONE object — see {@link MediaGalleryFieldBagProps}.
|
|
15
22
|
*/
|
|
16
23
|
import { useRef, useState } from "react";
|
|
17
24
|
import { Button, Space, Typography } from "antd";
|
|
@@ -58,6 +65,10 @@ function GalleryBody(props) {
|
|
|
58
65
|
}) }), _jsx(Space, { wrap: true, align: "start", children: bag.items.length === 0 ? (_jsx(Typography.Text, { type: "secondary", "data-testid": "cdn-gallery-empty", children: t(CDN_I18N_KEYS.galleryEmpty) })) : (bag.items.map((item, index) => (_jsx(Tile, { item: item, index: index, bag: bag, onDragStart: setDragging, onDrop: onDrop }, item.id)))) }), _jsx("input", { ref: input, type: "file", multiple: true, accept: bag.accept.attribute, onChange: onPick, style: { display: "none" }, "data-testid": "cdn-gallery-input" }), _jsx(Button, { onClick: () => input.current?.click(), disabled: addGate.disabled, "data-testid": "cdn-gallery-add", "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", children: t(CDN_I18N_KEYS.pickImages) }), addGate.reason === undefined ? null : (_jsx(Typography.Text, { type: "secondary", "data-testid": "cdn-gallery-add-blocked", children: addGate.reason })), settledGate.reason === undefined ? null : (_jsx(Typography.Text, { type: "secondary", "data-testid": "cdn-gallery-unsettled", children: settledGate.reason }))] }));
|
|
59
66
|
}
|
|
60
67
|
export function MediaGalleryField(props) {
|
|
68
|
+
// A queue handed in is drawn directly: no `MediaUploader`, because mounting
|
|
69
|
+
// one would create the SECOND queue this prop exists to prevent.
|
|
70
|
+
if (props.bag !== undefined)
|
|
71
|
+
return _jsx(GalleryBody, { bag: props.bag });
|
|
61
72
|
return (_jsx(MediaUploader, { max: props.max, ...(props.target !== undefined ? { target: props.target } : {}), ...(props.initialRefs !== undefined ? { initialRefs: props.initialRefs } : {}), ...(props.onRefsChange !== undefined
|
|
62
73
|
? { onRefsChange: props.onRefsChange }
|
|
63
74
|
: {}), children: (bag) => _jsx(GalleryBody, { bag: bag }) }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaGalleryField.js","sourceRoot":"","sources":["../../src/default/MediaGalleryField.tsx"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"MediaGalleryField.js","sourceRoot":"","sources":["../../src/default/MediaGalleryField.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEzC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAGnE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA4CrD,SAAS,IAAI,CAAC,KAMb;IACC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,YAAY,GAAG,eAAe,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IACjE,MAAM,OAAO,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IACnC,MAAM,IAAI,GACR,IAAI,CAAC,KAAK,KAAK,SAAS;QACxB,IAAI,CAAC,KAAK,KAAK,UAAU;QACzB,IAAI,CAAC,KAAK,KAAK,WAAW;QAC1B,IAAI,CAAC,KAAK,KAAK,YAAY,CAAC;IAE9B,OAAO,CACL,eACE,SAAS,QACT,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,EAC3C,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,EAC7C,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,iBACrB,kBAAkB,gBAClB,IAAI,CAAC,KAAK,EACtB,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE,aAElC,OAAO,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,CACtB,cAAK,KAAK,EAAE,EAAE,GAAG,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,GAAI,CACzD,CAAC,CAAC,CAAC,CACF,cAAK,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,WAAW,GAAI,CAC7E,EACD,KAAC,UAAU,CAAC,IAAI,IAAC,IAAI,EAAC,WAAW,iBAAa,gBAAgB,YAC3D,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GACV,EACjB,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CACb,KAAC,UAAU,CAAC,IAAI,IAAC,IAAI,EAAC,WAAW,iBAAa,gBAAgB,YAC3D,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,GACX,CACnB,CAAC,CAAC,CAAC,IAAI,EACP,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CACd,KAAC,UAAU,CAAC,IAAI,IAAC,IAAI,EAAC,WAAW,iBAAa,kBAAkB,YAC7D,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,GACT,CACnB,CAAC,CAAC,CAAC,IAAI,EACR,KAAC,UAAU,IAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAC,gBAAgB,GAAG,EACvE,MAAC,KAAK,IAAC,IAAI,EAAC,OAAO,EAAC,IAAI,mBACrB,IAAI,CAAC,CAAC,CAAC,CACN,KAAC,MAAM,IACL,IAAI,EAAC,OAAO,EACZ,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,iBACtB,iBAAiB,oBACd,MAAM,2BACC,8DAAyD,YAE9E,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC,GACrB,CACV,CAAC,CAAC,CAAC,IAAI,EACP,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,CACtD,KAAC,MAAM,IACL,IAAI,EAAC,OAAO,EACZ,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,iBACrB,gBAAgB,oBACb,MAAM,2BACC,8DAAyD,YAE9E,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,GACpB,CACV,CAAC,CAAC,CAAC,IAAI,EACR,KAAC,MAAM,IACL,IAAI,EAAC,OAAO,EACZ,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,iBACtB,iBAAiB,oBACd,MAAM,2BACC,8DAAyD,YAE9E,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC,GACrB,EACT,KAAC,MAAM,IACL,IAAI,EAAC,OAAO,EACZ,QAAQ,EAAE,KAAK,KAAK,CAAC,EACrB,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,iBAChC,kBAAkB,oBACf,MAAM,2BACC,8DAAyD,YAE9E,CAAC,CAAC,aAAa,CAAC,eAAe,CAAC,GAC1B,EACT,KAAC,MAAM,IACL,IAAI,EAAC,OAAO,EACZ,QAAQ,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EACxC,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,iBAChC,gBAAgB,oBACb,MAAM,2BACC,8DAAyD,YAE9E,CAAC,CAAC,aAAa,CAAC,aAAa,CAAC,GACxB,IACH,IACJ,CACP,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,KAA8B;IACjD,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC;IACtB,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC/C,MAAM,KAAK,GAAG,MAAM,CAAmB,IAAI,CAAC,CAAC;IAC7C,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAE9D,MAAM,MAAM,GAAG,CAAC,KAAoC,EAAQ,EAAE;QAC5D,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC;QACjC,KAAK,CAAC,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC;QACxB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACjD,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7B,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,CAAC,KAAa,EAAQ,EAAE;QACrC,IAAI,QAAQ,KAAK,IAAI;YAAE,OAAO;QAC9B,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC7B,WAAW,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC,CAAC;IAEF,OAAO,CACL,MAAC,KAAK,IAAC,SAAS,EAAC,UAAU,iBAAa,aAAa,aACnD,KAAC,UAAU,CAAC,IAAI,mBAAa,mBAAmB,YAC7C,CAAC,CAAC,aAAa,CAAC,YAAY,EAAE;oBAC7B,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI;oBACvB,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,GAAG;iBACtB,CAAC,GACc,EAClB,KAAC,KAAK,IAAC,IAAI,QAAC,KAAK,EAAC,OAAO,YACtB,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACxB,KAAC,UAAU,CAAC,IAAI,IAAC,IAAI,EAAC,WAAW,iBAAa,mBAAmB,YAC9D,CAAC,CAAC,aAAa,CAAC,YAAY,CAAC,GACd,CACnB,CAAC,CAAC,CAAC,CACF,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAC7B,KAAC,IAAI,IAEH,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,GAAG,EACR,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,IALT,IAAI,CAAC,EAAE,CAMZ,CACH,CAAC,CACH,GACK,EACR,gBACE,GAAG,EAAE,KAAK,EACV,IAAI,EAAC,MAAM,EACX,QAAQ,QACR,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,EAC5B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,iBACd,mBAAmB,GAC/B,EACF,KAAC,MAAM,IACL,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,EACrC,QAAQ,EAAE,OAAO,CAAC,QAAQ,iBACd,iBAAiB,oBACd,MAAM,2BACC,8DAAyD,YAE9E,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC,GACrB,EACR,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACrC,KAAC,UAAU,CAAC,IAAI,IAAC,IAAI,EAAC,WAAW,iBAAa,yBAAyB,YACpE,OAAO,CAAC,MAAM,GACC,CACnB,EACA,WAAW,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACzC,KAAC,UAAU,CAAC,IAAI,IAAC,IAAI,EAAC,WAAW,iBAAa,uBAAuB,YAClE,WAAW,CAAC,MAAM,GACH,CACnB,IACK,CACT,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAA6B;IAC7D,4EAA4E;IAC5E,iEAAiE;IACjE,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS;QAAE,OAAO,KAAC,WAAW,IAAC,GAAG,EAAE,KAAK,CAAC,GAAG,GAAI,CAAC;IACpE,OAAO,CACL,KAAC,aAAa,IACZ,GAAG,EAAE,KAAK,CAAC,GAAG,KACV,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAC5D,CAAC,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAC3E,CAAC,KAAK,CAAC,YAAY,KAAK,SAAS;YACnC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE;YACtC,CAAC,CAAC,EAAE,CAAC,YAEN,CAAC,GAAG,EAAE,EAAE,CAAC,KAAC,WAAW,IAAC,GAAG,EAAE,GAAG,GAAI,GACrB,CACjB,CAAC;AACJ,CAAC"}
|
package/dist/default/index.d.ts
CHANGED
|
@@ -11,5 +11,5 @@
|
|
|
11
11
|
export { ImageUploadField } from "./ImageUploadField.js";
|
|
12
12
|
export type { ImageUploadFieldProps } from "./ImageUploadField.js";
|
|
13
13
|
export { MediaGalleryField } from "./MediaGalleryField.js";
|
|
14
|
-
export type { MediaGalleryFieldProps } from "./MediaGalleryField.js";
|
|
14
|
+
export type { MediaGalleryFieldProps, MediaGalleryFieldBagProps, MediaGalleryFieldOwnProps, } from "./MediaGalleryField.js";
|
|
15
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EACV,sBAAsB,EACtB,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,wBAAwB,CAAC"}
|
package/llms.txt
CHANGED
package/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stapel/cdn-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Headless React pair for stapel-cdn: a typed media-upload client whose flow is dedup-first — SHA-256 the bytes, ask file/exists/, and skip the POST entirely when the CDN already holds them. A phase-shaped upload bag (validate → hash → check → upload → variants), an ordered queue with per-item cancel/retry/remove and a max, object-URL previews with the revoke built in (@stapel/core's useObjectUrlPreview), and the opaque `<type>/<hash>` reference every consuming module actually stores. Client-side limits mirror the backend's own declared ceilings and never refuse what the server would accept. Zero visual opinion in the main entry; an opt-in /default subpath ships the antd skin.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"size-limit": "^11.2.0",
|
|
99
99
|
"typescript": "^5.8.3",
|
|
100
100
|
"vitest": "^3.2.4",
|
|
101
|
-
"@stapel/core": "^0.
|
|
101
|
+
"@stapel/core": "^0.16.0",
|
|
102
102
|
"@stapel/image": "^0.2.0",
|
|
103
103
|
"@stapel/showcase": "^0.2.0",
|
|
104
104
|
"@stapel/tokens": "^0.5.0",
|
|
@@ -11,6 +11,13 @@
|
|
|
11
11
|
* The first tile is labelled as the cover, because the order IS the meaning:
|
|
12
12
|
* `Listing.images_draft` is stored in this order and the first reference is
|
|
13
13
|
* what a search result card shows.
|
|
14
|
+
*
|
|
15
|
+
* ── Whose queue is it ──────────────────────────────────────────────────────
|
|
16
|
+
*
|
|
17
|
+
* Either the caller's (`bag`) or this field's (`max`). A composer consumes
|
|
18
|
+
* `bag.refs` as `images_draft` and `bag.settled` as its publish gate, so when
|
|
19
|
+
* a composer is on the page the bag it holds and the bag drawn here must be
|
|
20
|
+
* ONE object — see {@link MediaGalleryFieldBagProps}.
|
|
14
21
|
*/
|
|
15
22
|
import { useRef, useState } from "react";
|
|
16
23
|
import type { ChangeEvent, ReactElement } from "react";
|
|
@@ -25,7 +32,29 @@ import { CDN_I18N_KEYS } from "../i18n/keys.js";
|
|
|
25
32
|
import { ErrorAlert } from "./ErrorAlert.js";
|
|
26
33
|
import { PHASE_KEYS, PREVIEW_BOX } from "./phase.js";
|
|
27
34
|
|
|
28
|
-
|
|
35
|
+
/**
|
|
36
|
+
* The gallery over a queue the CALLER owns.
|
|
37
|
+
*
|
|
38
|
+
* This is the shape a composer needs and the one the field did not have. A
|
|
39
|
+
* listing composer takes `bag.refs` as `images_draft` and `bag.settled` as its
|
|
40
|
+
* publish gate, so the bag it was handed and the bag the gallery draws MUST be
|
|
41
|
+
* the same object. When the field built its own, the container had two queues:
|
|
42
|
+
* the composer's (empty, because nothing was ever added to it) and the one on
|
|
43
|
+
* screen — so the publish gate said "wait for the photos" about photos it
|
|
44
|
+
* could not see, and `images_draft` went out empty.
|
|
45
|
+
*/
|
|
46
|
+
export interface MediaGalleryFieldBagProps {
|
|
47
|
+
/** The queue to draw. Hand it the same bag the composer got. */
|
|
48
|
+
bag: UploadQueueBag;
|
|
49
|
+
max?: undefined;
|
|
50
|
+
target?: undefined;
|
|
51
|
+
initialRefs?: undefined;
|
|
52
|
+
onRefsChange?: undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The gallery that owns its own queue — a field standing alone. */
|
|
56
|
+
export interface MediaGalleryFieldOwnProps {
|
|
57
|
+
bag?: undefined;
|
|
29
58
|
/** How many photos this gallery holds. The storefront's composer: 10. */
|
|
30
59
|
max: number;
|
|
31
60
|
target?: CdnUploadTarget;
|
|
@@ -35,6 +64,16 @@ export interface MediaGalleryFieldProps {
|
|
|
35
64
|
onRefsChange?: (refs: readonly CdnRef[]) => void;
|
|
36
65
|
}
|
|
37
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Either the caller owns the queue (`bag`) or this field does (`max` and the
|
|
69
|
+
* options that configure one). Spelled as a union rather than as optional
|
|
70
|
+
* props so that passing both — two queues, one screen, the defect above — is a
|
|
71
|
+
* type error rather than a decision this component has to make at runtime.
|
|
72
|
+
*/
|
|
73
|
+
export type MediaGalleryFieldProps =
|
|
74
|
+
| MediaGalleryFieldBagProps
|
|
75
|
+
| MediaGalleryFieldOwnProps;
|
|
76
|
+
|
|
38
77
|
function Tile(props: {
|
|
39
78
|
item: UploadItem;
|
|
40
79
|
index: number;
|
|
@@ -218,6 +257,9 @@ function GalleryBody(props: { bag: UploadQueueBag }): ReactElement {
|
|
|
218
257
|
}
|
|
219
258
|
|
|
220
259
|
export function MediaGalleryField(props: MediaGalleryFieldProps): ReactElement {
|
|
260
|
+
// A queue handed in is drawn directly: no `MediaUploader`, because mounting
|
|
261
|
+
// one would create the SECOND queue this prop exists to prevent.
|
|
262
|
+
if (props.bag !== undefined) return <GalleryBody bag={props.bag} />;
|
|
221
263
|
return (
|
|
222
264
|
<MediaUploader
|
|
223
265
|
max={props.max}
|
package/src/default/index.ts
CHANGED
|
@@ -11,4 +11,8 @@
|
|
|
11
11
|
export { ImageUploadField } from "./ImageUploadField.js";
|
|
12
12
|
export type { ImageUploadFieldProps } from "./ImageUploadField.js";
|
|
13
13
|
export { MediaGalleryField } from "./MediaGalleryField.js";
|
|
14
|
-
export type {
|
|
14
|
+
export type {
|
|
15
|
+
MediaGalleryFieldProps,
|
|
16
|
+
MediaGalleryFieldBagProps,
|
|
17
|
+
MediaGalleryFieldOwnProps,
|
|
18
|
+
} from "./MediaGalleryField.js";
|