@dloizides/ui-media 0.2.0 → 0.4.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 +10 -0
- package/dist/framing.d.mts +13 -19
- package/dist/framing.d.ts +13 -19
- package/dist/framing.js +17 -16
- package/dist/framing.js.map +1 -1
- package/dist/framing.mjs +17 -16
- package/dist/framing.mjs.map +1 -1
- package/dist/index.d.mts +87 -21
- package/dist/index.d.ts +87 -21
- package/dist/index.js +138 -51
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +136 -54
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
- KEFI-PEOPLE-1 T10d "Organizer people editor: anchored photo in the package": new `AnchoredPhoto` (`uri`, `anchor`, `framing`, `style`, `accessibilityLabel`, `testID`) paints a contained, framed photo anchored to the centre or bottom of its parent box. RN-web's Image ignores `object-position`, so the photo is sized to its measured aspect (`useImageAspectRatio`, exported) and placed by flexbox. Pure `resolveAnchoredPhotoLayout` exported. Moved out of kefi-web `PersonPhoto`.
|
|
6
|
+
|
|
7
|
+
## 0.3.0
|
|
8
|
+
|
|
9
|
+
- KEFI-PEOPLE-1 T10b "Organizer people editor: ui-media adoption gaps" (backwards compatible):
|
|
10
|
+
- `ImagePickerButton` / `useImageUpload` take a second generic `TResult` (default `void`): `upload` returns `Promise<TResult>` and the new `onUploaded(result)` receives it once the slot is `done`. A throwing `onUploaded` does not flip the slot to error.
|
|
11
|
+
- `PhotoFramingEditor` gains `anchor?: PhotoAnchor` (`center` default | `bottom`); `renderPreview` now also receives `anchor` and `objectPosition` (`center bottom` for kefi-landings `.amb-photo` cut-outs). New exports `PhotoAnchor`, `resolvePhotoAnchor`, `PhotoAnchorLayout`.
|
|
12
|
+
|
|
3
13
|
## 0.2.0
|
|
4
14
|
|
|
5
15
|
- KEFI-PEOPLE-1 T12 "Organizer people editor: shared photo framing": `PHOTO_FRAMING_BOUNDS`,
|
package/dist/framing.d.mts
CHANGED
|
@@ -2,12 +2,11 @@ import { PhotoFraming, PhotoFramingInput } from '@dloizides/site-template-kit/fr
|
|
|
2
2
|
export { DEFAULT_PHOTO_FRAMING, PHOTO_FRAMING_BOUNDS, PhotoFraming, PhotoFramingInput, clampFraming, isDefaultFraming } from '@dloizides/site-template-kit/framing';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Photo framing: the `{x, y, scale}` an
|
|
5
|
+
* Photo framing: the `{x, y, scale}` an editor sets on a photo card.
|
|
6
6
|
*
|
|
7
7
|
* The bounds, default, clamp and identity check come from
|
|
8
|
-
* `@dloizides/site-template-kit/framing` — the same module the
|
|
9
|
-
* renderer builds its `--photo-tf` CSS from
|
|
10
|
-
* editor: shared photo framing"). One copy, so the editor can never offer a value
|
|
8
|
+
* `@dloizides/site-template-kit/framing` — the same module the static landing
|
|
9
|
+
* renderer builds its `--photo-tf` CSS from. One copy, so the editor can never offer a value
|
|
11
10
|
* the landing page would silently clamp away.
|
|
12
11
|
*
|
|
13
12
|
* That subpath imports nothing (no ajv, no JSON Schema), so this file stays
|
|
@@ -32,21 +31,16 @@ type PhotoFramingTransform = [
|
|
|
32
31
|
];
|
|
33
32
|
declare const framingToTransform: (framing: PhotoFraming) => PhotoFramingTransform;
|
|
34
33
|
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
readonly MoveLeft: "moveLeft";
|
|
46
|
-
readonly MoveRight: "moveRight";
|
|
47
|
-
readonly Reset: "reset";
|
|
48
|
-
};
|
|
49
|
-
type FramingAction = (typeof FramingAction)[keyof typeof FramingAction];
|
|
34
|
+
/** The seven controls of the framing editor. */
|
|
35
|
+
declare const enum FramingAction {
|
|
36
|
+
ZoomIn = "zoomIn",
|
|
37
|
+
ZoomOut = "zoomOut",
|
|
38
|
+
MoveUp = "moveUp",
|
|
39
|
+
MoveDown = "moveDown",
|
|
40
|
+
MoveLeft = "moveLeft",
|
|
41
|
+
MoveRight = "moveRight",
|
|
42
|
+
Reset = "reset"
|
|
43
|
+
}
|
|
50
44
|
|
|
51
45
|
/** How far one press moves or zooms. */
|
|
52
46
|
interface PhotoFramingSteps {
|
package/dist/framing.d.ts
CHANGED
|
@@ -2,12 +2,11 @@ import { PhotoFraming, PhotoFramingInput } from '@dloizides/site-template-kit/fr
|
|
|
2
2
|
export { DEFAULT_PHOTO_FRAMING, PHOTO_FRAMING_BOUNDS, PhotoFraming, PhotoFramingInput, clampFraming, isDefaultFraming } from '@dloizides/site-template-kit/framing';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Photo framing: the `{x, y, scale}` an
|
|
5
|
+
* Photo framing: the `{x, y, scale}` an editor sets on a photo card.
|
|
6
6
|
*
|
|
7
7
|
* The bounds, default, clamp and identity check come from
|
|
8
|
-
* `@dloizides/site-template-kit/framing` — the same module the
|
|
9
|
-
* renderer builds its `--photo-tf` CSS from
|
|
10
|
-
* editor: shared photo framing"). One copy, so the editor can never offer a value
|
|
8
|
+
* `@dloizides/site-template-kit/framing` — the same module the static landing
|
|
9
|
+
* renderer builds its `--photo-tf` CSS from. One copy, so the editor can never offer a value
|
|
11
10
|
* the landing page would silently clamp away.
|
|
12
11
|
*
|
|
13
12
|
* That subpath imports nothing (no ajv, no JSON Schema), so this file stays
|
|
@@ -32,21 +31,16 @@ type PhotoFramingTransform = [
|
|
|
32
31
|
];
|
|
33
32
|
declare const framingToTransform: (framing: PhotoFraming) => PhotoFramingTransform;
|
|
34
33
|
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
readonly MoveLeft: "moveLeft";
|
|
46
|
-
readonly MoveRight: "moveRight";
|
|
47
|
-
readonly Reset: "reset";
|
|
48
|
-
};
|
|
49
|
-
type FramingAction = (typeof FramingAction)[keyof typeof FramingAction];
|
|
34
|
+
/** The seven controls of the framing editor. */
|
|
35
|
+
declare const enum FramingAction {
|
|
36
|
+
ZoomIn = "zoomIn",
|
|
37
|
+
ZoomOut = "zoomOut",
|
|
38
|
+
MoveUp = "moveUp",
|
|
39
|
+
MoveDown = "moveDown",
|
|
40
|
+
MoveLeft = "moveLeft",
|
|
41
|
+
MoveRight = "moveRight",
|
|
42
|
+
Reset = "reset"
|
|
43
|
+
}
|
|
50
44
|
|
|
51
45
|
/** How far one press moves or zooms. */
|
|
52
46
|
interface PhotoFramingSteps {
|
package/dist/framing.js
CHANGED
|
@@ -10,30 +10,31 @@ var framingToTransform = (framing) => [
|
|
|
10
10
|
];
|
|
11
11
|
|
|
12
12
|
// src/framing/FramingAction.ts
|
|
13
|
-
var FramingAction = {
|
|
14
|
-
ZoomIn
|
|
15
|
-
ZoomOut
|
|
16
|
-
MoveUp
|
|
17
|
-
MoveDown
|
|
18
|
-
MoveLeft
|
|
19
|
-
MoveRight
|
|
20
|
-
Reset
|
|
21
|
-
|
|
13
|
+
var FramingAction = /* @__PURE__ */ ((FramingAction2) => {
|
|
14
|
+
FramingAction2["ZoomIn"] = "zoomIn";
|
|
15
|
+
FramingAction2["ZoomOut"] = "zoomOut";
|
|
16
|
+
FramingAction2["MoveUp"] = "moveUp";
|
|
17
|
+
FramingAction2["MoveDown"] = "moveDown";
|
|
18
|
+
FramingAction2["MoveLeft"] = "moveLeft";
|
|
19
|
+
FramingAction2["MoveRight"] = "moveRight";
|
|
20
|
+
FramingAction2["Reset"] = "reset";
|
|
21
|
+
return FramingAction2;
|
|
22
|
+
})(FramingAction || {});
|
|
22
23
|
|
|
23
24
|
// src/framing/stepFraming.ts
|
|
24
25
|
var PHOTO_FRAMING_STEPS = Object.freeze({ offsetPct: 5, scale: 0.1 });
|
|
25
26
|
var PRECISION = 1e3;
|
|
26
27
|
var round = (n) => Math.round(n * PRECISION) / PRECISION;
|
|
27
28
|
var DIRECTIONS = {
|
|
28
|
-
[
|
|
29
|
-
[
|
|
30
|
-
[
|
|
31
|
-
[
|
|
32
|
-
[
|
|
33
|
-
[
|
|
29
|
+
["zoomIn" /* ZoomIn */]: [0, 0, 1],
|
|
30
|
+
["zoomOut" /* ZoomOut */]: [0, 0, -1],
|
|
31
|
+
["moveUp" /* MoveUp */]: [0, -1, 0],
|
|
32
|
+
["moveDown" /* MoveDown */]: [0, 1, 0],
|
|
33
|
+
["moveLeft" /* MoveLeft */]: [-1, 0, 0],
|
|
34
|
+
["moveRight" /* MoveRight */]: [1, 0, 0]
|
|
34
35
|
};
|
|
35
36
|
function stepFraming(current, action, steps = PHOTO_FRAMING_STEPS) {
|
|
36
|
-
if (action ===
|
|
37
|
+
if (action === "reset" /* Reset */) return framing.DEFAULT_PHOTO_FRAMING;
|
|
37
38
|
const base = framing.clampFraming(current);
|
|
38
39
|
const [dx, dy, ds] = DIRECTIONS[action];
|
|
39
40
|
return framing.clampFraming({
|
package/dist/framing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/framing/photoFraming.ts","../src/framing/FramingAction.ts","../src/framing/stepFraming.ts"],"names":["DEFAULT_PHOTO_FRAMING","clampFraming"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"sources":["../src/framing/photoFraming.ts","../src/framing/FramingAction.ts","../src/framing/stepFraming.ts"],"names":["FramingAction","DEFAULT_PHOTO_FRAMING","clampFraming"],"mappings":";;;;;AAgCO,IAAM,kBAAA,GAAqB,CAAC,OAAA,KAAiD;AAAA,EAClF,EAAE,UAAA,EAAY,CAAA,EAAG,OAAA,CAAQ,CAAC,CAAA,CAAA,CAAA,EAAI;AAAA,EAC9B,EAAE,UAAA,EAAY,CAAA,EAAG,OAAA,CAAQ,CAAC,CAAA,CAAA,CAAA,EAAI;AAAA,EAC9B,EAAE,KAAA,EAAO,OAAA,CAAQ,KAAA;AACnB;;;ACnCO,IAAW,aAAA,qBAAAA,cAAAA,KAAX;AACL,EAAAA,eAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,eAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,eAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,eAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,eAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,eAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,eAAA,OAAA,CAAA,GAAQ,OAAA;AAPQ,EAAA,OAAAA,cAAAA;AAAA,CAAA,EAAA,aAAA,IAAA,EAAA;;;ACWX,IAAM,mBAAA,GAAyC,OAAO,MAAA,CAAO,EAAE,WAAW,CAAA,EAAG,KAAA,EAAO,KAAK;AAGhG,IAAM,SAAA,GAAY,GAAA;AAClB,IAAM,QAAQ,CAAC,CAAA,KAAsB,KAAK,KAAA,CAAM,CAAA,GAAI,SAAS,CAAA,GAAI,SAAA;AAKjE,IAAM,UAAA,GAAoE;AAAA,EACxE,CAAA,QAAA,gBAAwB,CAAC,CAAA,EAAG,GAAG,CAAC,CAAA;AAAA,EAChC,CAAA,SAAA,iBAAyB,CAAC,CAAA,EAAG,GAAG,EAAE,CAAA;AAAA,EAClC,CAAA,QAAA,gBAAwB,CAAC,CAAA,EAAG,IAAI,CAAC,CAAA;AAAA,EACjC,CAAA,UAAA,kBAA0B,CAAC,CAAA,EAAG,GAAG,CAAC,CAAA;AAAA,EAClC,CAAA,UAAA,kBAA0B,CAAC,EAAA,EAAI,GAAG,CAAC,CAAA;AAAA,EACnC,CAAA,WAAA,mBAA2B,CAAC,CAAA,EAAG,GAAG,CAAC;AACrC,CAAA;AAMO,SAAS,WAAA,CACd,OAAA,EACA,MAAA,EACA,KAAA,GAA2B,mBAAA,EACb;AACd,EAAA,IAAI,gCAAgC,OAAOC,6BAAA;AAC3C,EAAA,MAAM,IAAA,GAAOC,qBAAa,OAAO,CAAA;AACjC,EAAA,MAAM,CAAC,EAAA,EAAI,EAAA,EAAI,EAAE,CAAA,GAAI,WAAW,MAAM,CAAA;AACtC,EAAA,OAAOA,oBAAA,CAAa;AAAA,IAClB,GAAG,KAAA,CAAM,IAAA,CAAK,CAAA,GAAI,EAAA,GAAK,MAAM,SAAS,CAAA;AAAA,IACtC,GAAG,KAAA,CAAM,IAAA,CAAK,CAAA,GAAI,EAAA,GAAK,MAAM,SAAS,CAAA;AAAA,IACtC,OAAO,KAAA,CAAM,IAAA,CAAK,KAAA,GAAQ,EAAA,GAAK,MAAM,KAAK;AAAA,GAC3C,CAAA;AACH;AAGO,SAAS,cAAA,CACd,OAAA,EACA,MAAA,EACA,KAAA,GAA2B,mBAAA,EAClB;AACT,EAAA,MAAM,IAAA,GAAOA,qBAAa,OAAO,CAAA;AACjC,EAAA,MAAM,IAAA,GAAO,WAAA,CAAY,IAAA,EAAM,MAAA,EAAQ,KAAK,CAAA;AAC5C,EAAA,OAAO,IAAA,CAAK,CAAA,KAAM,IAAA,CAAK,CAAA,IAAK,IAAA,CAAK,MAAM,IAAA,CAAK,CAAA,IAAK,IAAA,CAAK,KAAA,KAAU,IAAA,CAAK,KAAA;AACvE","file":"framing.js","sourcesContent":["/**\n * Photo framing: the `{x, y, scale}` an editor sets on a photo card.\n *\n * The bounds, default, clamp and identity check come from\n * `@dloizides/site-template-kit/framing` — the same module the static landing\n * renderer builds its `--photo-tf` CSS from. One copy, so the editor can never offer a value\n * the landing page would silently clamp away.\n *\n * That subpath imports nothing (no ajv, no JSON Schema), so this file stays\n * framework-free: it backs the `@dloizides/ui-media/framing` subpath.\n */\nimport type { PhotoFraming } from '@dloizides/site-template-kit/framing';\n\nexport {\n PHOTO_FRAMING_BOUNDS,\n DEFAULT_PHOTO_FRAMING,\n clampFraming,\n isDefaultFraming,\n} from '@dloizides/site-template-kit/framing';\nexport type { PhotoFraming, PhotoFramingInput } from '@dloizides/site-template-kit/framing';\n\n/**\n * An RN `transform` array. Percent translates are relative to the element\n * itself on RN-web, matching the landing page's CSS `translate(x%, y%) scale(s)`.\n * Typed locally so this module stays free of a react-native import.\n */\nexport type PhotoFramingTransform = [\n { translateX: `${number}%` },\n { translateY: `${number}%` },\n { scale: number },\n];\n\nexport const framingToTransform = (framing: PhotoFraming): PhotoFramingTransform => [\n { translateX: `${framing.x}%` },\n { translateY: `${framing.y}%` },\n { scale: framing.scale },\n];\n","/** The seven controls of the framing editor. */\nexport const enum FramingAction {\n ZoomIn = 'zoomIn',\n ZoomOut = 'zoomOut',\n MoveUp = 'moveUp',\n MoveDown = 'moveDown',\n MoveLeft = 'moveLeft',\n MoveRight = 'moveRight',\n Reset = 'reset',\n}\n","import { FramingAction } from './FramingAction';\nimport type { PhotoFraming, PhotoFramingInput } from './photoFraming';\nimport { DEFAULT_PHOTO_FRAMING, clampFraming } from './photoFraming';\n\n/** How far one press moves or zooms. */\nexport interface PhotoFramingSteps {\n /** Nudge per press, % of the card. */\n offsetPct: number;\n /** Zoom per press, as a scale delta. */\n scale: number;\n}\n\nexport const PHOTO_FRAMING_STEPS: PhotoFramingSteps = Object.freeze({ offsetPct: 5, scale: 0.1 });\n\n/** Three decimals, the same precision the landing page writes into its CSS. */\nconst PRECISION = 1000;\nconst round = (n: number): number => Math.round(n * PRECISION) / PRECISION;\n\ntype MoveAction = Exclude<FramingAction, FramingAction.Reset>;\n\n/** [x, y, scale] direction of each non-reset action. */\nconst DIRECTIONS: Record<MoveAction, readonly [number, number, number]> = {\n [FramingAction.ZoomIn]: [0, 0, 1],\n [FramingAction.ZoomOut]: [0, 0, -1],\n [FramingAction.MoveUp]: [0, -1, 0],\n [FramingAction.MoveDown]: [0, 1, 0],\n [FramingAction.MoveLeft]: [-1, 0, 0],\n [FramingAction.MoveRight]: [1, 0, 0],\n};\n\n/**\n * Applies one control press. The result is always clamped and rounded, so ten\n * presses of +0.1 land on exactly 2, not 1.9999999999999998.\n */\nexport function stepFraming(\n current: PhotoFramingInput | null | undefined,\n action: FramingAction,\n steps: PhotoFramingSteps = PHOTO_FRAMING_STEPS,\n): PhotoFraming {\n if (action === FramingAction.Reset) return DEFAULT_PHOTO_FRAMING;\n const base = clampFraming(current);\n const [dx, dy, ds] = DIRECTIONS[action];\n return clampFraming({\n x: round(base.x + dx * steps.offsetPct),\n y: round(base.y + dy * steps.offsetPct),\n scale: round(base.scale + ds * steps.scale),\n });\n}\n\n/** False when the press would change nothing (at a bound, or reset on the default). */\nexport function canStepFraming(\n current: PhotoFramingInput | null | undefined,\n action: FramingAction,\n steps: PhotoFramingSteps = PHOTO_FRAMING_STEPS,\n): boolean {\n const base = clampFraming(current);\n const next = stepFraming(base, action, steps);\n return next.x !== base.x || next.y !== base.y || next.scale !== base.scale;\n}\n"]}
|
package/dist/framing.mjs
CHANGED
|
@@ -9,30 +9,31 @@ var framingToTransform = (framing) => [
|
|
|
9
9
|
];
|
|
10
10
|
|
|
11
11
|
// src/framing/FramingAction.ts
|
|
12
|
-
var FramingAction = {
|
|
13
|
-
ZoomIn
|
|
14
|
-
ZoomOut
|
|
15
|
-
MoveUp
|
|
16
|
-
MoveDown
|
|
17
|
-
MoveLeft
|
|
18
|
-
MoveRight
|
|
19
|
-
Reset
|
|
20
|
-
|
|
12
|
+
var FramingAction = /* @__PURE__ */ ((FramingAction2) => {
|
|
13
|
+
FramingAction2["ZoomIn"] = "zoomIn";
|
|
14
|
+
FramingAction2["ZoomOut"] = "zoomOut";
|
|
15
|
+
FramingAction2["MoveUp"] = "moveUp";
|
|
16
|
+
FramingAction2["MoveDown"] = "moveDown";
|
|
17
|
+
FramingAction2["MoveLeft"] = "moveLeft";
|
|
18
|
+
FramingAction2["MoveRight"] = "moveRight";
|
|
19
|
+
FramingAction2["Reset"] = "reset";
|
|
20
|
+
return FramingAction2;
|
|
21
|
+
})(FramingAction || {});
|
|
21
22
|
|
|
22
23
|
// src/framing/stepFraming.ts
|
|
23
24
|
var PHOTO_FRAMING_STEPS = Object.freeze({ offsetPct: 5, scale: 0.1 });
|
|
24
25
|
var PRECISION = 1e3;
|
|
25
26
|
var round = (n) => Math.round(n * PRECISION) / PRECISION;
|
|
26
27
|
var DIRECTIONS = {
|
|
27
|
-
[
|
|
28
|
-
[
|
|
29
|
-
[
|
|
30
|
-
[
|
|
31
|
-
[
|
|
32
|
-
[
|
|
28
|
+
["zoomIn" /* ZoomIn */]: [0, 0, 1],
|
|
29
|
+
["zoomOut" /* ZoomOut */]: [0, 0, -1],
|
|
30
|
+
["moveUp" /* MoveUp */]: [0, -1, 0],
|
|
31
|
+
["moveDown" /* MoveDown */]: [0, 1, 0],
|
|
32
|
+
["moveLeft" /* MoveLeft */]: [-1, 0, 0],
|
|
33
|
+
["moveRight" /* MoveRight */]: [1, 0, 0]
|
|
33
34
|
};
|
|
34
35
|
function stepFraming(current, action, steps = PHOTO_FRAMING_STEPS) {
|
|
35
|
-
if (action ===
|
|
36
|
+
if (action === "reset" /* Reset */) return DEFAULT_PHOTO_FRAMING;
|
|
36
37
|
const base = clampFraming(current);
|
|
37
38
|
const [dx, dy, ds] = DIRECTIONS[action];
|
|
38
39
|
return clampFraming({
|
package/dist/framing.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/framing/photoFraming.ts","../src/framing/FramingAction.ts","../src/framing/stepFraming.ts"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"sources":["../src/framing/photoFraming.ts","../src/framing/FramingAction.ts","../src/framing/stepFraming.ts"],"names":["FramingAction"],"mappings":";;;;AAgCO,IAAM,kBAAA,GAAqB,CAAC,OAAA,KAAiD;AAAA,EAClF,EAAE,UAAA,EAAY,CAAA,EAAG,OAAA,CAAQ,CAAC,CAAA,CAAA,CAAA,EAAI;AAAA,EAC9B,EAAE,UAAA,EAAY,CAAA,EAAG,OAAA,CAAQ,CAAC,CAAA,CAAA,CAAA,EAAI;AAAA,EAC9B,EAAE,KAAA,EAAO,OAAA,CAAQ,KAAA;AACnB;;;ACnCO,IAAW,aAAA,qBAAAA,cAAAA,KAAX;AACL,EAAAA,eAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,eAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,eAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,eAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,eAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,eAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,eAAA,OAAA,CAAA,GAAQ,OAAA;AAPQ,EAAA,OAAAA,cAAAA;AAAA,CAAA,EAAA,aAAA,IAAA,EAAA;;;ACWX,IAAM,mBAAA,GAAyC,OAAO,MAAA,CAAO,EAAE,WAAW,CAAA,EAAG,KAAA,EAAO,KAAK;AAGhG,IAAM,SAAA,GAAY,GAAA;AAClB,IAAM,QAAQ,CAAC,CAAA,KAAsB,KAAK,KAAA,CAAM,CAAA,GAAI,SAAS,CAAA,GAAI,SAAA;AAKjE,IAAM,UAAA,GAAoE;AAAA,EACxE,CAAA,QAAA,gBAAwB,CAAC,CAAA,EAAG,GAAG,CAAC,CAAA;AAAA,EAChC,CAAA,SAAA,iBAAyB,CAAC,CAAA,EAAG,GAAG,EAAE,CAAA;AAAA,EAClC,CAAA,QAAA,gBAAwB,CAAC,CAAA,EAAG,IAAI,CAAC,CAAA;AAAA,EACjC,CAAA,UAAA,kBAA0B,CAAC,CAAA,EAAG,GAAG,CAAC,CAAA;AAAA,EAClC,CAAA,UAAA,kBAA0B,CAAC,EAAA,EAAI,GAAG,CAAC,CAAA;AAAA,EACnC,CAAA,WAAA,mBAA2B,CAAC,CAAA,EAAG,GAAG,CAAC;AACrC,CAAA;AAMO,SAAS,WAAA,CACd,OAAA,EACA,MAAA,EACA,KAAA,GAA2B,mBAAA,EACb;AACd,EAAA,IAAI,gCAAgC,OAAO,qBAAA;AAC3C,EAAA,MAAM,IAAA,GAAO,aAAa,OAAO,CAAA;AACjC,EAAA,MAAM,CAAC,EAAA,EAAI,EAAA,EAAI,EAAE,CAAA,GAAI,WAAW,MAAM,CAAA;AACtC,EAAA,OAAO,YAAA,CAAa;AAAA,IAClB,GAAG,KAAA,CAAM,IAAA,CAAK,CAAA,GAAI,EAAA,GAAK,MAAM,SAAS,CAAA;AAAA,IACtC,GAAG,KAAA,CAAM,IAAA,CAAK,CAAA,GAAI,EAAA,GAAK,MAAM,SAAS,CAAA;AAAA,IACtC,OAAO,KAAA,CAAM,IAAA,CAAK,KAAA,GAAQ,EAAA,GAAK,MAAM,KAAK;AAAA,GAC3C,CAAA;AACH;AAGO,SAAS,cAAA,CACd,OAAA,EACA,MAAA,EACA,KAAA,GAA2B,mBAAA,EAClB;AACT,EAAA,MAAM,IAAA,GAAO,aAAa,OAAO,CAAA;AACjC,EAAA,MAAM,IAAA,GAAO,WAAA,CAAY,IAAA,EAAM,MAAA,EAAQ,KAAK,CAAA;AAC5C,EAAA,OAAO,IAAA,CAAK,CAAA,KAAM,IAAA,CAAK,CAAA,IAAK,IAAA,CAAK,MAAM,IAAA,CAAK,CAAA,IAAK,IAAA,CAAK,KAAA,KAAU,IAAA,CAAK,KAAA;AACvE","file":"framing.mjs","sourcesContent":["/**\n * Photo framing: the `{x, y, scale}` an editor sets on a photo card.\n *\n * The bounds, default, clamp and identity check come from\n * `@dloizides/site-template-kit/framing` — the same module the static landing\n * renderer builds its `--photo-tf` CSS from. One copy, so the editor can never offer a value\n * the landing page would silently clamp away.\n *\n * That subpath imports nothing (no ajv, no JSON Schema), so this file stays\n * framework-free: it backs the `@dloizides/ui-media/framing` subpath.\n */\nimport type { PhotoFraming } from '@dloizides/site-template-kit/framing';\n\nexport {\n PHOTO_FRAMING_BOUNDS,\n DEFAULT_PHOTO_FRAMING,\n clampFraming,\n isDefaultFraming,\n} from '@dloizides/site-template-kit/framing';\nexport type { PhotoFraming, PhotoFramingInput } from '@dloizides/site-template-kit/framing';\n\n/**\n * An RN `transform` array. Percent translates are relative to the element\n * itself on RN-web, matching the landing page's CSS `translate(x%, y%) scale(s)`.\n * Typed locally so this module stays free of a react-native import.\n */\nexport type PhotoFramingTransform = [\n { translateX: `${number}%` },\n { translateY: `${number}%` },\n { scale: number },\n];\n\nexport const framingToTransform = (framing: PhotoFraming): PhotoFramingTransform => [\n { translateX: `${framing.x}%` },\n { translateY: `${framing.y}%` },\n { scale: framing.scale },\n];\n","/** The seven controls of the framing editor. */\nexport const enum FramingAction {\n ZoomIn = 'zoomIn',\n ZoomOut = 'zoomOut',\n MoveUp = 'moveUp',\n MoveDown = 'moveDown',\n MoveLeft = 'moveLeft',\n MoveRight = 'moveRight',\n Reset = 'reset',\n}\n","import { FramingAction } from './FramingAction';\nimport type { PhotoFraming, PhotoFramingInput } from './photoFraming';\nimport { DEFAULT_PHOTO_FRAMING, clampFraming } from './photoFraming';\n\n/** How far one press moves or zooms. */\nexport interface PhotoFramingSteps {\n /** Nudge per press, % of the card. */\n offsetPct: number;\n /** Zoom per press, as a scale delta. */\n scale: number;\n}\n\nexport const PHOTO_FRAMING_STEPS: PhotoFramingSteps = Object.freeze({ offsetPct: 5, scale: 0.1 });\n\n/** Three decimals, the same precision the landing page writes into its CSS. */\nconst PRECISION = 1000;\nconst round = (n: number): number => Math.round(n * PRECISION) / PRECISION;\n\ntype MoveAction = Exclude<FramingAction, FramingAction.Reset>;\n\n/** [x, y, scale] direction of each non-reset action. */\nconst DIRECTIONS: Record<MoveAction, readonly [number, number, number]> = {\n [FramingAction.ZoomIn]: [0, 0, 1],\n [FramingAction.ZoomOut]: [0, 0, -1],\n [FramingAction.MoveUp]: [0, -1, 0],\n [FramingAction.MoveDown]: [0, 1, 0],\n [FramingAction.MoveLeft]: [-1, 0, 0],\n [FramingAction.MoveRight]: [1, 0, 0],\n};\n\n/**\n * Applies one control press. The result is always clamped and rounded, so ten\n * presses of +0.1 land on exactly 2, not 1.9999999999999998.\n */\nexport function stepFraming(\n current: PhotoFramingInput | null | undefined,\n action: FramingAction,\n steps: PhotoFramingSteps = PHOTO_FRAMING_STEPS,\n): PhotoFraming {\n if (action === FramingAction.Reset) return DEFAULT_PHOTO_FRAMING;\n const base = clampFraming(current);\n const [dx, dy, ds] = DIRECTIONS[action];\n return clampFraming({\n x: round(base.x + dx * steps.offsetPct),\n y: round(base.y + dy * steps.offsetPct),\n scale: round(base.scale + ds * steps.scale),\n });\n}\n\n/** False when the press would change nothing (at a bound, or reset on the default). */\nexport function canStepFraming(\n current: PhotoFramingInput | null | undefined,\n action: FramingAction,\n steps: PhotoFramingSteps = PHOTO_FRAMING_STEPS,\n): boolean {\n const base = clampFraming(current);\n const next = stepFraming(base, action, steps);\n return next.x !== base.x || next.y !== base.y || next.scale !== base.scale;\n}\n"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -4,19 +4,15 @@ import React from 'react';
|
|
|
4
4
|
import { ButtonVariant } from '@dloizides/ui-buttons';
|
|
5
5
|
import { PhotoFramingInput, PhotoFraming } from '@dloizides/site-template-kit/framing';
|
|
6
6
|
export { DEFAULT_PHOTO_FRAMING, PHOTO_FRAMING_BOUNDS, PhotoFraming, PhotoFramingInput, clampFraming, isDefaultFraming } from '@dloizides/site-template-kit/framing';
|
|
7
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
7
8
|
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
readonly Uploading: "uploading";
|
|
16
|
-
readonly Error: "error";
|
|
17
|
-
readonly Done: "done";
|
|
18
|
-
};
|
|
19
|
-
type ImagePickerStatus = (typeof ImagePickerStatus)[keyof typeof ImagePickerStatus];
|
|
9
|
+
/** The four states of an image upload slot. */
|
|
10
|
+
declare const enum ImagePickerStatus {
|
|
11
|
+
Idle = "idle",
|
|
12
|
+
Uploading = "uploading",
|
|
13
|
+
Error = "error",
|
|
14
|
+
Done = "done"
|
|
15
|
+
}
|
|
20
16
|
|
|
21
17
|
/**
|
|
22
18
|
* ImagePickerButton — one upload slot: pick an image, hand it to the consumer's
|
|
@@ -42,8 +38,11 @@ interface ImagePickerLabels {
|
|
|
42
38
|
/** Accessibility hint for the button: "Opens a file picker to choose an image". */
|
|
43
39
|
hint: string;
|
|
44
40
|
}
|
|
45
|
-
interface ImagePickerButtonProps<TFile = File> {
|
|
46
|
-
|
|
41
|
+
interface ImagePickerButtonProps<TFile = File, TResult = void> {
|
|
42
|
+
/** Consumer transport; whatever it resolves with (e.g. the stored URL) goes to `onUploaded`. */
|
|
43
|
+
upload: (file: TFile) => Promise<TResult>;
|
|
44
|
+
/** Called with `upload`'s result once the slot is `done`. */
|
|
45
|
+
onUploaded?: (result: TResult) => void;
|
|
47
46
|
labels: ImagePickerLabels;
|
|
48
47
|
testID: string;
|
|
49
48
|
/** Defaults to the browser file dialog. Native consumers pass their own picker. */
|
|
@@ -64,7 +63,7 @@ interface PickerView {
|
|
|
64
63
|
}
|
|
65
64
|
/** Pure status -> copy/variant mapping (unit-tested; the component only paints it). */
|
|
66
65
|
declare function resolvePickerView(status: ImagePickerStatus, labels: ImagePickerLabels): PickerView;
|
|
67
|
-
declare function ImagePickerButton<TFile = File>({ upload, labels, testID, pickFile, accept, hasImage, onError, disabled, }: ImagePickerButtonProps<TFile>): React.ReactElement;
|
|
66
|
+
declare function ImagePickerButton<TFile = File, TResult = void>({ upload, onUploaded, labels, testID, pickFile, accept, hasImage, onError, disabled, }: ImagePickerButtonProps<TFile, TResult>): React.ReactElement;
|
|
68
67
|
|
|
69
68
|
declare const DEFAULT_IMAGE_ACCEPT = "image/*";
|
|
70
69
|
/**
|
|
@@ -72,9 +71,11 @@ declare const DEFAULT_IMAGE_ACCEPT = "image/*";
|
|
|
72
71
|
* where there is no DOM (native) — a native consumer passes its own `pickFile`.
|
|
73
72
|
*/
|
|
74
73
|
declare function pickWebImage(accept?: string): Promise<File | null>;
|
|
75
|
-
interface UseImageUploadOptions<TFile> {
|
|
74
|
+
interface UseImageUploadOptions<TFile, TResult = void> {
|
|
76
75
|
/** Consumer-owned transport. Resolve = stored; reject = error state. No network code lives here. */
|
|
77
|
-
upload: (file: TFile) => Promise<
|
|
76
|
+
upload: (file: TFile) => Promise<TResult>;
|
|
77
|
+
/** Given what `upload` resolved with (e.g. the stored URL), once the slot is `done`. */
|
|
78
|
+
onUploaded?: (result: TResult) => void;
|
|
78
79
|
/** Picks one file; `null` means the user cancelled. */
|
|
79
80
|
pickFile: () => Promise<TFile | null>;
|
|
80
81
|
/** Start in `done` when the slot already holds an image. */
|
|
@@ -92,7 +93,25 @@ interface ImageUploadState {
|
|
|
92
93
|
* done or error back to uploading on the next pick. A cancelled pick leaves the
|
|
93
94
|
* state as it was, so cancelling "Replace" does not wipe a finished upload.
|
|
94
95
|
*/
|
|
95
|
-
declare function useImageUpload<TFile>({ upload, pickFile, hasImage, onError, }: UseImageUploadOptions<TFile>): ImageUploadState;
|
|
96
|
+
declare function useImageUpload<TFile, TResult = void>({ upload, pickFile, hasImage, onError, onUploaded, }: UseImageUploadOptions<TFile, TResult>): ImageUploadState;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Where the photo sits inside its frame. `Bottom` is for cut-out portraits
|
|
100
|
+
* (`object-fit: contain; object-position: center bottom`): feet on the floor of
|
|
101
|
+
* the card.
|
|
102
|
+
*/
|
|
103
|
+
declare const enum PhotoAnchor {
|
|
104
|
+
Center = "center",
|
|
105
|
+
Bottom = "bottom"
|
|
106
|
+
}
|
|
107
|
+
interface PhotoAnchorLayout {
|
|
108
|
+
/** Applied to the editor's preview column: where the consumer's card sits vertically. */
|
|
109
|
+
justifyContent: 'center' | 'flex-end';
|
|
110
|
+
/** CSS `object-position` the consumer spreads onto its `<img>` / RN-web image. */
|
|
111
|
+
objectPosition: 'center center' | 'center bottom';
|
|
112
|
+
}
|
|
113
|
+
/** Pure anchor -> layout mapping (unit-tested; the editor only applies it). */
|
|
114
|
+
declare function resolvePhotoAnchor(anchor: PhotoAnchor): PhotoAnchorLayout;
|
|
96
115
|
|
|
97
116
|
/** Name + hint of one control. `label` is the full phrase ("Move photo up"). */
|
|
98
117
|
interface FramingActionLabel {
|
|
@@ -114,6 +133,10 @@ interface PhotoFramingPreviewArgs {
|
|
|
114
133
|
framing: PhotoFraming;
|
|
115
134
|
/** Ready to spread onto the photo: `style={{ transform }}`. */
|
|
116
135
|
transform: PhotoFramingTransform;
|
|
136
|
+
/** The editor's `anchor` prop (default `center`). */
|
|
137
|
+
anchor: PhotoAnchor;
|
|
138
|
+
/** CSS `object-position` for that anchor, ready for the photo's style. */
|
|
139
|
+
objectPosition: string;
|
|
117
140
|
}
|
|
118
141
|
interface PhotoFramingEditorProps {
|
|
119
142
|
/** Stored framing; clamped before use, so raw config is safe to pass. */
|
|
@@ -128,10 +151,12 @@ interface PhotoFramingEditorProps {
|
|
|
128
151
|
/** Override the default arrow / plus / minus glyphs (e.g. with ui-icons). */
|
|
129
152
|
renderGlyph?: (action: FramingAction, color: string, size: number) => React.ReactNode;
|
|
130
153
|
disabled?: boolean;
|
|
154
|
+
/** Where the photo sits in the preview. `bottom` for cut-out portraits. Default `center`. */
|
|
155
|
+
anchor?: PhotoAnchor;
|
|
131
156
|
}
|
|
132
157
|
|
|
133
158
|
/**
|
|
134
|
-
* PhotoFramingEditor —
|
|
159
|
+
* PhotoFramingEditor — a photo framing screen: a live preview of
|
|
135
160
|
* the consumer's own card (render prop) beside a zoom stepper and a position
|
|
136
161
|
* d-pad. The value is `{x, y, scale}`, clamped to PHOTO_FRAMING_BOUNDS, the same
|
|
137
162
|
* rules the public landing page applies — the preview is what ships.
|
|
@@ -140,6 +165,47 @@ interface PhotoFramingEditorProps {
|
|
|
140
165
|
* LAYOUT_COLLAPSE_BREAKPOINT up they sit side by side.
|
|
141
166
|
*/
|
|
142
167
|
|
|
143
|
-
declare const PhotoFramingEditor: ({ value, onChange, renderPreview, labels, testID, steps, renderGlyph, disabled, }: PhotoFramingEditorProps) => React.ReactElement;
|
|
168
|
+
declare const PhotoFramingEditor: ({ value, onChange, renderPreview, labels, testID, steps, renderGlyph, disabled, anchor, }: PhotoFramingEditorProps) => React.ReactElement;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* AnchoredPhoto — a contained photo that fills its parent box, framed by a
|
|
172
|
+
* `{x, y, scale}` value and anchored to the centre or the bottom edge.
|
|
173
|
+
*
|
|
174
|
+
* Why not `object-position`: RN-web's Image paints a centred background image,
|
|
175
|
+
* so `center bottom` has no effect. Instead the image is sized to its own aspect
|
|
176
|
+
* ratio (measured with `Image.getSize`) and pushed down by flexbox. The framing
|
|
177
|
+
* transform sits on a full-box layer, so its percentages mean the same as the
|
|
178
|
+
* CSS `translate(x%, y%) scale(s)` a static page applies to its `<img>`.
|
|
179
|
+
*
|
|
180
|
+
* The parent owns the box (aspect, radius, background, empty state); this renders
|
|
181
|
+
* only the photo layer. No strings of its own: `accessibilityLabel` is a prop.
|
|
182
|
+
*/
|
|
183
|
+
|
|
184
|
+
interface AnchoredPhotoProps {
|
|
185
|
+
uri: string;
|
|
186
|
+
/** `bottom` for cut-out portraits. Default `center`. */
|
|
187
|
+
anchor?: PhotoAnchor;
|
|
188
|
+
/** Stored or live framing; clamped before use, so raw config is safe. */
|
|
189
|
+
framing?: PhotoFramingInput | null;
|
|
190
|
+
/** Extra style for the full-box frame layer. */
|
|
191
|
+
style?: StyleProp<ViewStyle>;
|
|
192
|
+
accessibilityLabel: string;
|
|
193
|
+
testID?: string;
|
|
194
|
+
}
|
|
195
|
+
declare const AnchoredPhoto: ({ uri, anchor, framing, style, accessibilityLabel, testID, }: AnchoredPhotoProps) => React.ReactElement;
|
|
196
|
+
|
|
197
|
+
declare function useImageAspectRatio(uri: string): number | null;
|
|
198
|
+
|
|
199
|
+
interface AnchoredPhotoLayout {
|
|
200
|
+
/** Main-axis placement of the photo inside its full-box frame. */
|
|
201
|
+
justifyContent: PhotoAnchorLayout['justifyContent'];
|
|
202
|
+
/**
|
|
203
|
+
* `null` = fill the box (size unknown yet, image stays centred by `contain`);
|
|
204
|
+
* a number = size the image to that width / height so the frame can anchor it.
|
|
205
|
+
*/
|
|
206
|
+
aspectRatio: number | null;
|
|
207
|
+
}
|
|
208
|
+
/** Pure anchor + measured aspect -> layout (unit-tested; AnchoredPhoto only paints it). */
|
|
209
|
+
declare function resolveAnchoredPhotoLayout(anchor: PhotoAnchor, aspectRatio: number | null): AnchoredPhotoLayout;
|
|
144
210
|
|
|
145
|
-
export { DEFAULT_IMAGE_ACCEPT, FramingAction, type FramingActionLabel, ImagePickerButton, type ImagePickerButtonProps, type ImagePickerLabels, ImagePickerStatus, type ImageUploadState, PhotoFramingEditor, type PhotoFramingEditorProps, type PhotoFramingLabels, type PhotoFramingPreviewArgs, PhotoFramingSteps, PhotoFramingTransform, type PickerView, type UseImageUploadOptions, pickWebImage, resolvePickerView, useImageUpload };
|
|
211
|
+
export { AnchoredPhoto, type AnchoredPhotoLayout, type AnchoredPhotoProps, DEFAULT_IMAGE_ACCEPT, FramingAction, type FramingActionLabel, ImagePickerButton, type ImagePickerButtonProps, type ImagePickerLabels, ImagePickerStatus, type ImageUploadState, PhotoAnchor, type PhotoAnchorLayout, PhotoFramingEditor, type PhotoFramingEditorProps, type PhotoFramingLabels, type PhotoFramingPreviewArgs, PhotoFramingSteps, PhotoFramingTransform, type PickerView, type UseImageUploadOptions, pickWebImage, resolveAnchoredPhotoLayout, resolvePhotoAnchor, resolvePickerView, useImageAspectRatio, useImageUpload };
|
package/dist/index.d.ts
CHANGED
|
@@ -4,19 +4,15 @@ import React from 'react';
|
|
|
4
4
|
import { ButtonVariant } from '@dloizides/ui-buttons';
|
|
5
5
|
import { PhotoFramingInput, PhotoFraming } from '@dloizides/site-template-kit/framing';
|
|
6
6
|
export { DEFAULT_PHOTO_FRAMING, PHOTO_FRAMING_BOUNDS, PhotoFraming, PhotoFramingInput, clampFraming, isDefaultFraming } from '@dloizides/site-template-kit/framing';
|
|
7
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
7
8
|
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
readonly Uploading: "uploading";
|
|
16
|
-
readonly Error: "error";
|
|
17
|
-
readonly Done: "done";
|
|
18
|
-
};
|
|
19
|
-
type ImagePickerStatus = (typeof ImagePickerStatus)[keyof typeof ImagePickerStatus];
|
|
9
|
+
/** The four states of an image upload slot. */
|
|
10
|
+
declare const enum ImagePickerStatus {
|
|
11
|
+
Idle = "idle",
|
|
12
|
+
Uploading = "uploading",
|
|
13
|
+
Error = "error",
|
|
14
|
+
Done = "done"
|
|
15
|
+
}
|
|
20
16
|
|
|
21
17
|
/**
|
|
22
18
|
* ImagePickerButton — one upload slot: pick an image, hand it to the consumer's
|
|
@@ -42,8 +38,11 @@ interface ImagePickerLabels {
|
|
|
42
38
|
/** Accessibility hint for the button: "Opens a file picker to choose an image". */
|
|
43
39
|
hint: string;
|
|
44
40
|
}
|
|
45
|
-
interface ImagePickerButtonProps<TFile = File> {
|
|
46
|
-
|
|
41
|
+
interface ImagePickerButtonProps<TFile = File, TResult = void> {
|
|
42
|
+
/** Consumer transport; whatever it resolves with (e.g. the stored URL) goes to `onUploaded`. */
|
|
43
|
+
upload: (file: TFile) => Promise<TResult>;
|
|
44
|
+
/** Called with `upload`'s result once the slot is `done`. */
|
|
45
|
+
onUploaded?: (result: TResult) => void;
|
|
47
46
|
labels: ImagePickerLabels;
|
|
48
47
|
testID: string;
|
|
49
48
|
/** Defaults to the browser file dialog. Native consumers pass their own picker. */
|
|
@@ -64,7 +63,7 @@ interface PickerView {
|
|
|
64
63
|
}
|
|
65
64
|
/** Pure status -> copy/variant mapping (unit-tested; the component only paints it). */
|
|
66
65
|
declare function resolvePickerView(status: ImagePickerStatus, labels: ImagePickerLabels): PickerView;
|
|
67
|
-
declare function ImagePickerButton<TFile = File>({ upload, labels, testID, pickFile, accept, hasImage, onError, disabled, }: ImagePickerButtonProps<TFile>): React.ReactElement;
|
|
66
|
+
declare function ImagePickerButton<TFile = File, TResult = void>({ upload, onUploaded, labels, testID, pickFile, accept, hasImage, onError, disabled, }: ImagePickerButtonProps<TFile, TResult>): React.ReactElement;
|
|
68
67
|
|
|
69
68
|
declare const DEFAULT_IMAGE_ACCEPT = "image/*";
|
|
70
69
|
/**
|
|
@@ -72,9 +71,11 @@ declare const DEFAULT_IMAGE_ACCEPT = "image/*";
|
|
|
72
71
|
* where there is no DOM (native) — a native consumer passes its own `pickFile`.
|
|
73
72
|
*/
|
|
74
73
|
declare function pickWebImage(accept?: string): Promise<File | null>;
|
|
75
|
-
interface UseImageUploadOptions<TFile> {
|
|
74
|
+
interface UseImageUploadOptions<TFile, TResult = void> {
|
|
76
75
|
/** Consumer-owned transport. Resolve = stored; reject = error state. No network code lives here. */
|
|
77
|
-
upload: (file: TFile) => Promise<
|
|
76
|
+
upload: (file: TFile) => Promise<TResult>;
|
|
77
|
+
/** Given what `upload` resolved with (e.g. the stored URL), once the slot is `done`. */
|
|
78
|
+
onUploaded?: (result: TResult) => void;
|
|
78
79
|
/** Picks one file; `null` means the user cancelled. */
|
|
79
80
|
pickFile: () => Promise<TFile | null>;
|
|
80
81
|
/** Start in `done` when the slot already holds an image. */
|
|
@@ -92,7 +93,25 @@ interface ImageUploadState {
|
|
|
92
93
|
* done or error back to uploading on the next pick. A cancelled pick leaves the
|
|
93
94
|
* state as it was, so cancelling "Replace" does not wipe a finished upload.
|
|
94
95
|
*/
|
|
95
|
-
declare function useImageUpload<TFile>({ upload, pickFile, hasImage, onError, }: UseImageUploadOptions<TFile>): ImageUploadState;
|
|
96
|
+
declare function useImageUpload<TFile, TResult = void>({ upload, pickFile, hasImage, onError, onUploaded, }: UseImageUploadOptions<TFile, TResult>): ImageUploadState;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Where the photo sits inside its frame. `Bottom` is for cut-out portraits
|
|
100
|
+
* (`object-fit: contain; object-position: center bottom`): feet on the floor of
|
|
101
|
+
* the card.
|
|
102
|
+
*/
|
|
103
|
+
declare const enum PhotoAnchor {
|
|
104
|
+
Center = "center",
|
|
105
|
+
Bottom = "bottom"
|
|
106
|
+
}
|
|
107
|
+
interface PhotoAnchorLayout {
|
|
108
|
+
/** Applied to the editor's preview column: where the consumer's card sits vertically. */
|
|
109
|
+
justifyContent: 'center' | 'flex-end';
|
|
110
|
+
/** CSS `object-position` the consumer spreads onto its `<img>` / RN-web image. */
|
|
111
|
+
objectPosition: 'center center' | 'center bottom';
|
|
112
|
+
}
|
|
113
|
+
/** Pure anchor -> layout mapping (unit-tested; the editor only applies it). */
|
|
114
|
+
declare function resolvePhotoAnchor(anchor: PhotoAnchor): PhotoAnchorLayout;
|
|
96
115
|
|
|
97
116
|
/** Name + hint of one control. `label` is the full phrase ("Move photo up"). */
|
|
98
117
|
interface FramingActionLabel {
|
|
@@ -114,6 +133,10 @@ interface PhotoFramingPreviewArgs {
|
|
|
114
133
|
framing: PhotoFraming;
|
|
115
134
|
/** Ready to spread onto the photo: `style={{ transform }}`. */
|
|
116
135
|
transform: PhotoFramingTransform;
|
|
136
|
+
/** The editor's `anchor` prop (default `center`). */
|
|
137
|
+
anchor: PhotoAnchor;
|
|
138
|
+
/** CSS `object-position` for that anchor, ready for the photo's style. */
|
|
139
|
+
objectPosition: string;
|
|
117
140
|
}
|
|
118
141
|
interface PhotoFramingEditorProps {
|
|
119
142
|
/** Stored framing; clamped before use, so raw config is safe to pass. */
|
|
@@ -128,10 +151,12 @@ interface PhotoFramingEditorProps {
|
|
|
128
151
|
/** Override the default arrow / plus / minus glyphs (e.g. with ui-icons). */
|
|
129
152
|
renderGlyph?: (action: FramingAction, color: string, size: number) => React.ReactNode;
|
|
130
153
|
disabled?: boolean;
|
|
154
|
+
/** Where the photo sits in the preview. `bottom` for cut-out portraits. Default `center`. */
|
|
155
|
+
anchor?: PhotoAnchor;
|
|
131
156
|
}
|
|
132
157
|
|
|
133
158
|
/**
|
|
134
|
-
* PhotoFramingEditor —
|
|
159
|
+
* PhotoFramingEditor — a photo framing screen: a live preview of
|
|
135
160
|
* the consumer's own card (render prop) beside a zoom stepper and a position
|
|
136
161
|
* d-pad. The value is `{x, y, scale}`, clamped to PHOTO_FRAMING_BOUNDS, the same
|
|
137
162
|
* rules the public landing page applies — the preview is what ships.
|
|
@@ -140,6 +165,47 @@ interface PhotoFramingEditorProps {
|
|
|
140
165
|
* LAYOUT_COLLAPSE_BREAKPOINT up they sit side by side.
|
|
141
166
|
*/
|
|
142
167
|
|
|
143
|
-
declare const PhotoFramingEditor: ({ value, onChange, renderPreview, labels, testID, steps, renderGlyph, disabled, }: PhotoFramingEditorProps) => React.ReactElement;
|
|
168
|
+
declare const PhotoFramingEditor: ({ value, onChange, renderPreview, labels, testID, steps, renderGlyph, disabled, anchor, }: PhotoFramingEditorProps) => React.ReactElement;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* AnchoredPhoto — a contained photo that fills its parent box, framed by a
|
|
172
|
+
* `{x, y, scale}` value and anchored to the centre or the bottom edge.
|
|
173
|
+
*
|
|
174
|
+
* Why not `object-position`: RN-web's Image paints a centred background image,
|
|
175
|
+
* so `center bottom` has no effect. Instead the image is sized to its own aspect
|
|
176
|
+
* ratio (measured with `Image.getSize`) and pushed down by flexbox. The framing
|
|
177
|
+
* transform sits on a full-box layer, so its percentages mean the same as the
|
|
178
|
+
* CSS `translate(x%, y%) scale(s)` a static page applies to its `<img>`.
|
|
179
|
+
*
|
|
180
|
+
* The parent owns the box (aspect, radius, background, empty state); this renders
|
|
181
|
+
* only the photo layer. No strings of its own: `accessibilityLabel` is a prop.
|
|
182
|
+
*/
|
|
183
|
+
|
|
184
|
+
interface AnchoredPhotoProps {
|
|
185
|
+
uri: string;
|
|
186
|
+
/** `bottom` for cut-out portraits. Default `center`. */
|
|
187
|
+
anchor?: PhotoAnchor;
|
|
188
|
+
/** Stored or live framing; clamped before use, so raw config is safe. */
|
|
189
|
+
framing?: PhotoFramingInput | null;
|
|
190
|
+
/** Extra style for the full-box frame layer. */
|
|
191
|
+
style?: StyleProp<ViewStyle>;
|
|
192
|
+
accessibilityLabel: string;
|
|
193
|
+
testID?: string;
|
|
194
|
+
}
|
|
195
|
+
declare const AnchoredPhoto: ({ uri, anchor, framing, style, accessibilityLabel, testID, }: AnchoredPhotoProps) => React.ReactElement;
|
|
196
|
+
|
|
197
|
+
declare function useImageAspectRatio(uri: string): number | null;
|
|
198
|
+
|
|
199
|
+
interface AnchoredPhotoLayout {
|
|
200
|
+
/** Main-axis placement of the photo inside its full-box frame. */
|
|
201
|
+
justifyContent: PhotoAnchorLayout['justifyContent'];
|
|
202
|
+
/**
|
|
203
|
+
* `null` = fill the box (size unknown yet, image stays centred by `contain`);
|
|
204
|
+
* a number = size the image to that width / height so the frame can anchor it.
|
|
205
|
+
*/
|
|
206
|
+
aspectRatio: number | null;
|
|
207
|
+
}
|
|
208
|
+
/** Pure anchor + measured aspect -> layout (unit-tested; AnchoredPhoto only paints it). */
|
|
209
|
+
declare function resolveAnchoredPhotoLayout(anchor: PhotoAnchor, aspectRatio: number | null): AnchoredPhotoLayout;
|
|
144
210
|
|
|
145
|
-
export { DEFAULT_IMAGE_ACCEPT, FramingAction, type FramingActionLabel, ImagePickerButton, type ImagePickerButtonProps, type ImagePickerLabels, ImagePickerStatus, type ImageUploadState, PhotoFramingEditor, type PhotoFramingEditorProps, type PhotoFramingLabels, type PhotoFramingPreviewArgs, PhotoFramingSteps, PhotoFramingTransform, type PickerView, type UseImageUploadOptions, pickWebImage, resolvePickerView, useImageUpload };
|
|
211
|
+
export { AnchoredPhoto, type AnchoredPhotoLayout, type AnchoredPhotoProps, DEFAULT_IMAGE_ACCEPT, FramingAction, type FramingActionLabel, ImagePickerButton, type ImagePickerButtonProps, type ImagePickerLabels, ImagePickerStatus, type ImageUploadState, PhotoAnchor, type PhotoAnchorLayout, PhotoFramingEditor, type PhotoFramingEditorProps, type PhotoFramingLabels, type PhotoFramingPreviewArgs, PhotoFramingSteps, PhotoFramingTransform, type PickerView, type UseImageUploadOptions, pickWebImage, resolveAnchoredPhotoLayout, resolvePhotoAnchor, resolvePickerView, useImageAspectRatio, useImageUpload };
|