@octane-xplat/ui 0.10.0 → 0.11.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/dist/native/Image.tsrx.js +151 -16
- package/dist/native/hash-image.js +285 -0
- package/dist/native/image-content-binding.js +158 -0
- package/dist/native/image-content.js +206 -0
- package/dist/native/image-sizing.js +102 -0
- package/dist/native/octane-client-build.json +1 -1
- package/dist/native/vendor/ui-svg/index.android.js +1 -1
- package/dist/web/Image.web.tsrx.js +112 -19
- package/dist/web/hash-image.js +285 -0
- package/dist/web/image-content.js +133 -0
- package/dist/web/octane-client-build.json +1 -1
- package/package.json +3 -2
- package/src/Image.macos.tsrx +11 -2
- package/src/Image.tsrx +203 -17
- package/src/Image.web.tsrx +91 -5
- package/src/Tabs.macos.tsrx +2 -0
- package/src/Timestamp.macos.tsrx +1 -1
- package/src/hash-image.test.ts +72 -0
- package/src/hash-image.ts +310 -0
- package/src/hosted-unsupported.macos.tsrx +1 -2
- package/src/image-content-binding.mobile.test.ts +174 -0
- package/src/image-content-binding.ts +231 -0
- package/src/image-content.mobile.test.tsrx +84 -0
- package/src/image-content.test.ts +219 -0
- package/src/image-content.ts +309 -0
- package/src/image-decoding.web.test.tsrx +26 -0
- package/src/image-placeholder.mobile.test.tsrx +113 -0
- package/src/image-placeholder.web.test.tsrx +48 -0
- package/src/image-recycling.mobile.test.ts +61 -0
- package/src/image-sizing.mobile.test.ts +158 -0
- package/src/image-sizing.ts +117 -0
- package/src/image.mobile.test.tsrx +81 -0
- package/src/index.macos.ts +1 -0
- package/src/index.shared.ts +7 -0
- package/src/index.shared.web.ts +7 -0
- package/src/list-viewability-observer.ios.ts +1 -0
- package/src/list-viewability.mobile.test.ts +1 -0
- package/src/props.ts +100 -1
- package/src/safeAreaInsets.macos.tsrx +6 -1
- package/src/styled.macos.tsrx +1 -1
- package/src/vendor/ui-svg/src/index.android.ts +5 -1
- package/types/generated/.tsrx-typegen-manifest.json +170 -0
- package/types/generated/Accordion.macos.d.ts +5 -0
- package/types/generated/Alert.macos.d.ts +3 -0
- package/types/generated/AlertDialog.macos.d.ts +4 -0
- package/types/generated/AppShell.macos.d.ts +7 -0
- package/types/generated/AspectRatio.macos.d.ts +5 -0
- package/types/generated/Avatar.macos.d.ts +6 -0
- package/types/generated/AvatarGroup.macos.d.ts +6 -0
- package/types/generated/Badge.macos.d.ts +5 -0
- package/types/generated/Banner.macos.d.ts +4 -0
- package/types/generated/BaseTypeahead.macos.d.ts +7 -0
- package/types/generated/Blockquote.macos.d.ts +6 -0
- package/types/generated/BottomSheet.macos.d.ts +3 -0
- package/types/generated/Breadcrumb.macos.d.ts +4 -0
- package/types/generated/ButtonGroup.macos.d.ts +3 -0
- package/types/generated/Calendar.macos.d.ts +5 -0
- package/types/generated/Card.macos.d.ts +4 -0
- package/types/generated/Carousel.macos.d.ts +6 -0
- package/types/generated/Center.macos.d.ts +4 -0
- package/types/generated/CheckIndicator.macos.d.ts +4 -0
- package/types/generated/CheckboxGroup.macos.d.ts +3 -0
- package/types/generated/CheckboxIndicator.macos.d.ts +4 -0
- package/types/generated/Chip.macos.d.ts +5 -0
- package/types/generated/Citation.macos.d.ts +4 -0
- package/types/generated/ClickableCard.macos.d.ts +3 -0
- package/types/generated/Code.macos.d.ts +5 -0
- package/types/generated/CodeBlock.macos.d.ts +6 -0
- package/types/generated/Collapsible.macos.d.ts +5 -0
- package/types/generated/CommandPalette.macos.d.ts +3 -0
- package/types/generated/ComplexSelector.macos.d.ts +3 -0
- package/types/generated/ContextMenu.macos.d.ts +5 -0
- package/types/generated/DateInput.macos.d.ts +6 -0
- package/types/generated/DateRangeInput.macos.d.ts +5 -0
- package/types/generated/DateTimeInput.macos.d.ts +5 -0
- package/types/generated/Dialog.macos.d.ts +3 -0
- package/types/generated/Drawer.macos.d.ts +7 -0
- package/types/generated/DropdownMenu.macos.d.ts +5 -0
- package/types/generated/Empty.macos.d.ts +4 -0
- package/types/generated/FieldGroup.macos.d.ts +4 -0
- package/types/generated/FormField.macos.d.ts +4 -0
- package/types/generated/FormLayout.macos.d.ts +6 -0
- package/types/generated/HStack.macos.d.ts +4 -0
- package/types/generated/Heading.macos.d.ts +3 -0
- package/types/generated/Icon.macos.d.ts +3 -0
- package/types/generated/IconButton.macos.d.ts +3 -0
- package/types/generated/Image.d.ts +0 -5
- package/types/generated/Image.macos.d.ts +2 -0
- package/types/generated/Image.web.d.ts +12 -1
- package/types/generated/InputGroup.macos.d.ts +5 -0
- package/types/generated/InputNumber.macos.d.ts +5 -0
- package/types/generated/InputRating.macos.d.ts +4 -0
- package/types/generated/Item.macos.d.ts +3 -0
- package/types/generated/Kbd.macos.d.ts +4 -0
- package/types/generated/KeyboardAvoiding.macos.d.ts +3 -0
- package/types/generated/Lightbox.macos.d.ts +2 -0
- package/types/generated/Link.macos.d.ts +3 -0
- package/types/generated/List.macos.d.ts +4 -0
- package/types/generated/ListItem.macos.d.ts +5 -0
- package/types/generated/MenuParts.macos.d.ts +9 -0
- package/types/generated/MenuSurface.macos.d.ts +4 -0
- package/types/generated/MetadataList.macos.d.ts +4 -0
- package/types/generated/MetadataListItem.macos.d.ts +3 -0
- package/types/generated/Meter.macos.d.ts +3 -0
- package/types/generated/MobileNav.macos.d.ts +15 -0
- package/types/generated/MoreMenu.macos.d.ts +2 -0
- package/types/generated/NavIcon.macos.d.ts +4 -0
- package/types/generated/NavLink.macos.d.ts +3 -0
- package/types/generated/NavMenu.macos.d.ts +11 -0
- package/types/generated/NavigationMenu.macos.d.ts +4 -0
- package/types/generated/Outline.macos.d.ts +5 -0
- package/types/generated/OverflowList.macos.d.ts +6 -0
- package/types/generated/Overlay.macos.d.ts +3 -0
- package/types/generated/Pagination.macos.d.ts +4 -0
- package/types/generated/PinInput.macos.d.ts +4 -0
- package/types/generated/Popover.macos.d.ts +3 -0
- package/types/generated/PowerSearch.macos.d.ts +7 -0
- package/types/generated/PowerSearchFilterEditor.macos.d.ts +12 -0
- package/types/generated/PowerSearchToken.macos.d.ts +7 -0
- package/types/generated/Pressable.macos.d.ts +2 -0
- package/types/generated/ProgressBar.macos.d.ts +6 -0
- package/types/generated/ProgressGroup.macos.d.ts +3 -0
- package/types/generated/RadioGroup.macos.d.ts +5 -0
- package/types/generated/RadioIndicator.macos.d.ts +3 -0
- package/types/generated/ResizeHandle.macos.d.ts +7 -0
- package/types/generated/RouteBoundary.macos.d.ts +8 -0
- package/types/generated/RouteHost.macos.d.ts +13 -0
- package/types/generated/Row.macos.d.ts +2 -0
- package/types/generated/SafeArea.macos.d.ts +3 -0
- package/types/generated/Screen.macos.d.ts +3 -0
- package/types/generated/ScrollableArea.macos.d.ts +7 -0
- package/types/generated/Section.macos.d.ts +5 -0
- package/types/generated/Select.macos.d.ts +3 -0
- package/types/generated/SelectableCard.macos.d.ts +3 -0
- package/types/generated/Separator.macos.d.ts +5 -0
- package/types/generated/SideNav.macos.d.ts +19 -0
- package/types/generated/Skeleton.macos.d.ts +5 -0
- package/types/generated/StatusDot.macos.d.ts +5 -0
- package/types/generated/Stepper.macos.d.ts +4 -0
- package/types/generated/TabList.macos.d.ts +18 -0
- package/types/generated/Table.macos.d.ts +4 -0
- package/types/generated/Tabs.macos.d.ts +3 -0
- package/types/generated/Text.macos.d.ts +5 -0
- package/types/generated/Thumbnail.macos.d.ts +7 -0
- package/types/generated/TimeInput.macos.d.ts +6 -0
- package/types/generated/TimeWheelPanel.macos.d.ts +16 -0
- package/types/generated/Timeline.macos.d.ts +3 -0
- package/types/generated/Timer.macos.d.ts +3 -0
- package/types/generated/Timestamp.macos.d.ts +5 -0
- package/types/generated/Toast.macos.d.ts +3 -0
- package/types/generated/ToggleButton.macos.d.ts +3 -0
- package/types/generated/Token.macos.d.ts +5 -0
- package/types/generated/Tokenizer.macos.d.ts +6 -0
- package/types/generated/Toolbar.macos.d.ts +6 -0
- package/types/generated/TopNav.macos.d.ts +25 -0
- package/types/generated/Tree.macos.d.ts +4 -0
- package/types/generated/Typeahead.macos.d.ts +8 -0
- package/types/generated/TypeaheadItem.macos.d.ts +5 -0
- package/types/generated/User.macos.d.ts +5 -0
- package/types/generated/VStack.macos.d.ts +4 -0
- package/types/generated/View.macos.d.ts +3 -0
- package/types/generated/VirtualList.macos.d.ts +6 -0
- package/types/generated/VisuallyHidden.macos.d.ts +5 -0
- package/types/generated/WebView.macos.d.ts +2 -0
- package/types/generated/aliases.macos.d.ts +3 -0
- package/types/generated/anim.macos.d.ts +3 -0
- package/types/generated/animation-host.macos.d.ts +3 -0
- package/types/generated/bottom-sheet-panel.macos.d.ts +7 -0
- package/types/generated/bottom-sheet-service.macos.d.ts +5 -0
- package/types/generated/clipboard-write.macos.d.ts +3 -0
- package/types/generated/colorScheme.macos.d.ts +4 -0
- package/types/generated/command-palette-keys.macos.d.ts +2 -0
- package/types/generated/command-palette-scroll.macos.d.ts +2 -0
- package/types/generated/controls.macos.d.ts +8 -0
- package/types/generated/date-composition.macos.d.ts +5 -0
- package/types/generated/escape-props.macos.d.ts +5 -0
- package/types/generated/hash-image.d.ts +13 -0
- package/types/generated/hosted-unsupported.macos.d.ts +1 -0
- package/types/generated/image-content-binding.d.ts +64 -0
- package/types/generated/image-content.d.ts +56 -0
- package/types/generated/image-sizing.d.ts +37 -0
- package/types/generated/index.macos.d.ts +196 -0
- package/types/generated/index.shared.d.ts +1 -1
- package/types/generated/index.shared.web.d.ts +1 -1
- package/types/generated/layout-shells.macos.d.ts +3 -0
- package/types/generated/layout.macos.d.ts +9 -0
- package/types/generated/macos-extras.macos.d.ts +4 -0
- package/types/generated/measure-bounds.macos.d.ts +3 -0
- package/types/generated/nav-href.macos.d.ts +4 -0
- package/types/generated/outline-dom.macos.d.ts +2 -0
- package/types/generated/pan.macos.d.ts +3 -0
- package/types/generated/platform.macos.d.ts +1 -0
- package/types/generated/presentation.macos.d.ts +15 -0
- package/types/generated/props.d.ts +76 -1
- package/types/generated/query-storage.macos.d.ts +8 -0
- package/types/generated/resize-basis.macos.d.ts +4 -0
- package/types/generated/resize-persist.macos.d.ts +8 -0
- package/types/generated/route.macos.d.ts +28 -0
- package/types/generated/safeAreaInsets.macos.d.ts +10 -0
- package/types/generated/stacks.macos.d.ts +3 -0
- package/types/generated/styled.macos.d.ts +9 -0
- package/types/generated/surfaces.macos.d.ts +4 -0
- package/types/generated/svg-image.macos.d.ts +3 -0
- package/types/generated/text-controls.macos.d.ts +4 -0
- package/types/generated/theme/theme-scheme.macos.d.ts +8 -0
- package/types/generated/toast-service.macos.d.ts +3 -0
- package/types/generated/toast-viewport.macos.d.ts +5 -0
- package/types/generated/translate.macos.d.ts +1 -0
- package/types/generated/unsupported.macos.d.ts +1 -0
- package/types/generated/use-imperative-alert-dialog.macos.d.ts +2 -0
- package/types/generated/use-imperative-dialog.macos.d.ts +2 -0
- package/types/generated/use-lightbox.macos.d.ts +2 -0
- package/types/generated/use-scrollable-area.macos.d.ts +6 -0
- package/types/generated/useMeasure.macos.d.ts +3 -0
- package/types/generated/useResizable.macos.d.ts +8 -0
- package/types/generated/useStore.macos.d.ts +2 -0
- package/types/generated/windows.macos.d.ts +2 -0
- package/src/CameraView.macos.tsrx +0 -6
- package/types/index.macos.d.ts +0 -448
|
@@ -1,36 +1,125 @@
|
|
|
1
1
|
import { cx } from "./cx.js";
|
|
2
|
+
import { rememberGridChildPlacement } from "./grid-placement.js";
|
|
2
3
|
import { applyEscapeProps } from "./escape-props.js";
|
|
3
4
|
import { layoutChildProps } from "./layout-child.js";
|
|
4
5
|
import { isSvgSrc, svgSource } from "./svg-source.js";
|
|
5
6
|
import "./svg.mobile.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
7
|
+
import { createSizedImageBinding } from "./image-sizing.js";
|
|
8
|
+
import { contentFitToStretch, needsContentRect, resolveContentFit, resolveContentPosition } from "./image-content.js";
|
|
9
|
+
import { createImageContentBinding } from "./image-content-binding.js";
|
|
10
|
+
import { hashToPngBase64 } from "./hash-image.js";
|
|
11
|
+
import { defineUniversalComponent, hookSlots, universalIf, universalPlan, universalProps, universalValue, useLayoutEffect, useMemo, useRef } from "@nativescript-community/octane";
|
|
12
|
+
import { ImageSource, Screen, isAndroid, isIOS, knownFolders } from "@nativescript/core";
|
|
8
13
|
//#region src/Image.tsrx
|
|
9
|
-
var _hs$ = /* @__PURE__ */ hookSlots(
|
|
14
|
+
var _hs$ = /* @__PURE__ */ hookSlots(8);
|
|
10
15
|
var _h$0 = /* @__PURE__ */ Symbol(_hs$);
|
|
11
16
|
var _h$1 = /* @__PURE__ */ Symbol(_hs$ + 1);
|
|
17
|
+
var _h$2 = /* @__PURE__ */ Symbol(_hs$ + 2);
|
|
18
|
+
var _h$3 = /* @__PURE__ */ Symbol(_hs$ + 3);
|
|
19
|
+
var _h$4 = /* @__PURE__ */ Symbol(_hs$ + 4);
|
|
20
|
+
var _h$5 = /* @__PURE__ */ Symbol(_hs$ + 5);
|
|
21
|
+
var _h$6 = /* @__PURE__ */ Symbol(_hs$ + 6);
|
|
22
|
+
var _h$7 = /* @__PURE__ */ Symbol(_hs$ + 7);
|
|
12
23
|
var __octaneUniversalPlan0 = universalPlan("nativescript", {
|
|
13
24
|
"kind": "host",
|
|
14
25
|
"type": "svgview",
|
|
15
26
|
"propsSlot": 0
|
|
16
27
|
});
|
|
17
28
|
var __octaneUniversalPlan1 = universalPlan("nativescript", {
|
|
29
|
+
"kind": "host",
|
|
30
|
+
"type": "absolutelayout",
|
|
31
|
+
"propsSlot": 0,
|
|
32
|
+
"children": [{
|
|
33
|
+
"kind": "host",
|
|
34
|
+
"type": "image",
|
|
35
|
+
"props": { "stretch": "fill" },
|
|
36
|
+
"bindings": [
|
|
37
|
+
["ref", 1],
|
|
38
|
+
["loadMode", 2],
|
|
39
|
+
["accessibilityLabel", 3]
|
|
40
|
+
]
|
|
41
|
+
}]
|
|
42
|
+
});
|
|
43
|
+
var __octaneUniversalPlan2 = universalPlan("nativescript", {
|
|
18
44
|
"kind": "host",
|
|
19
45
|
"type": "image",
|
|
20
46
|
"propsSlot": 0
|
|
21
47
|
});
|
|
22
|
-
var
|
|
48
|
+
var __octaneUniversalPlan3 = universalPlan("nativescript", {
|
|
23
49
|
"kind": "slot",
|
|
24
50
|
"slot": 0
|
|
25
51
|
});
|
|
52
|
+
function placeholderSource(placeholder) {
|
|
53
|
+
const png = hashToPngBase64(placeholder);
|
|
54
|
+
if (png) return ImageSource.fromBase64Sync(png);
|
|
55
|
+
const comma = placeholder.indexOf(",");
|
|
56
|
+
if (placeholder.startsWith("data:") && comma > 0 && placeholder.slice(0, comma).endsWith(";base64")) return ImageSource.fromBase64Sync(placeholder.slice(comma + 1));
|
|
57
|
+
if (placeholder.startsWith("res://")) return ImageSource.fromResourceSync(placeholder.slice(6));
|
|
58
|
+
if (placeholder.startsWith("~/")) return ImageSource.fromFileSync(knownFolders.currentApp().path + "/" + placeholder.slice(2));
|
|
59
|
+
if (!/^https?:\/\//.test(placeholder)) return ImageSource.fromFileSync(placeholder);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
function applyPlaceholder(view, placeholder) {
|
|
63
|
+
if (!placeholder || !(view.isLoading || !view.imageSource)) return;
|
|
64
|
+
const ph = placeholderSource(placeholder);
|
|
65
|
+
if (ph) view.imageSource = ph;
|
|
66
|
+
}
|
|
67
|
+
function clipImageHost(host) {
|
|
68
|
+
host?.on?.("loaded", () => {
|
|
69
|
+
if (isIOS && host.ios) host.ios.clipsToBounds = true;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
26
72
|
var Image = defineUniversalComponent("nativescript", function Image(props) {
|
|
27
|
-
const
|
|
73
|
+
const sources = Array.isArray(props.src) ? props.src : null;
|
|
74
|
+
const srcString = sources ? "" : props.src;
|
|
75
|
+
const fit = resolveContentFit(props.contentFit);
|
|
76
|
+
const position = resolveContentPosition(props.contentPosition);
|
|
77
|
+
const computed = needsContentRect(fit, position);
|
|
78
|
+
const imageRef = useRef(null, _h$0);
|
|
79
|
+
const placeholderRef = useRef(props.placeholder, _h$1);
|
|
80
|
+
placeholderRef.current = props.placeholder;
|
|
81
|
+
const sizing = useMemo(() => createSizedImageBinding({ afterIssue: (v) => applyPlaceholder(v, placeholderRef.current) }), [], _h$2);
|
|
82
|
+
const binding = useMemo(() => createImageContentBinding({
|
|
83
|
+
isAndroid,
|
|
84
|
+
screenScale: () => Screen.mainScreen?.scale ?? 1,
|
|
85
|
+
issue: (view, src, gate) => sizing.update(view, src, gate)
|
|
86
|
+
}), [], _h$3);
|
|
87
|
+
const boundRef = useRef({
|
|
88
|
+
key: props.recyclingKey,
|
|
89
|
+
src: props.src
|
|
90
|
+
}, _h$4);
|
|
91
|
+
const bound = boundRef.current;
|
|
92
|
+
boundRef.current = {
|
|
93
|
+
key: props.recyclingKey,
|
|
94
|
+
src: props.src
|
|
95
|
+
};
|
|
96
|
+
if (bound.key != null && props.recyclingKey != null && bound.key !== props.recyclingKey && bound.src !== props.src && imageRef.current) imageRef.current.imageSource = null;
|
|
97
|
+
const svg = useMemo(() => srcString && isSvgSrc(srcString) ? svgSource(srcString) : null, [srcString], _h$5);
|
|
28
98
|
const source = useMemo(() => {
|
|
29
|
-
const comma =
|
|
30
|
-
if (
|
|
31
|
-
return
|
|
32
|
-
}, [
|
|
33
|
-
|
|
99
|
+
const comma = srcString.indexOf(",");
|
|
100
|
+
if (srcString.startsWith("data:") && comma > 0 && srcString.slice(0, comma).endsWith(";base64")) return ImageSource.fromBase64Sync(srcString.slice(comma + 1));
|
|
101
|
+
return srcString;
|
|
102
|
+
}, [srcString], _h$6);
|
|
103
|
+
const update = {
|
|
104
|
+
source: sources ? null : source,
|
|
105
|
+
sources,
|
|
106
|
+
fit,
|
|
107
|
+
position,
|
|
108
|
+
computed
|
|
109
|
+
};
|
|
110
|
+
useLayoutEffect(() => {
|
|
111
|
+
const view = imageRef.current;
|
|
112
|
+
if (!view || !props.placeholder || svg != null) return;
|
|
113
|
+
const show = () => applyPlaceholder(view, props.placeholder);
|
|
114
|
+
show();
|
|
115
|
+
view.on?.("isLoadingChange", show);
|
|
116
|
+
return () => view.off?.("isLoadingChange", show);
|
|
117
|
+
}, [
|
|
118
|
+
props.src,
|
|
119
|
+
props.placeholder,
|
|
120
|
+
svg
|
|
121
|
+
], _h$7);
|
|
122
|
+
return universalValue(__octaneUniversalPlan3, [universalIf(svg != null, () => {
|
|
34
123
|
return universalValue(__octaneUniversalPlan0, [universalProps([
|
|
35
124
|
[
|
|
36
125
|
"set",
|
|
@@ -61,7 +150,7 @@ var Image = defineUniversalComponent("nativescript", function Image(props) {
|
|
|
61
150
|
[
|
|
62
151
|
"set",
|
|
63
152
|
"stretch",
|
|
64
|
-
"
|
|
153
|
+
fit === "none" ? "none" : contentFitToStretch(fit === "scale-down" ? "contain" : fit)
|
|
65
154
|
],
|
|
66
155
|
[
|
|
67
156
|
"set",
|
|
@@ -69,12 +158,53 @@ var Image = defineUniversalComponent("nativescript", function Image(props) {
|
|
|
69
158
|
props.alt
|
|
70
159
|
]
|
|
71
160
|
])]);
|
|
161
|
+
}, () => universalIf(computed, () => {
|
|
162
|
+
return universalValue(__octaneUniversalPlan1, [
|
|
163
|
+
universalProps([
|
|
164
|
+
[
|
|
165
|
+
"set",
|
|
166
|
+
"ref",
|
|
167
|
+
(v) => {
|
|
168
|
+
clipImageHost(v);
|
|
169
|
+
rememberGridChildPlacement(v, props);
|
|
170
|
+
if (v) binding.update(v, null, update);
|
|
171
|
+
}
|
|
172
|
+
],
|
|
173
|
+
[
|
|
174
|
+
"set",
|
|
175
|
+
"id",
|
|
176
|
+
props.id
|
|
177
|
+
],
|
|
178
|
+
["spread", layoutChildProps(props)],
|
|
179
|
+
[
|
|
180
|
+
"set",
|
|
181
|
+
"className",
|
|
182
|
+
cx(props.className)
|
|
183
|
+
],
|
|
184
|
+
[
|
|
185
|
+
"set",
|
|
186
|
+
"style",
|
|
187
|
+
props.style
|
|
188
|
+
]
|
|
189
|
+
]),
|
|
190
|
+
(v) => {
|
|
191
|
+
imageRef.current = v;
|
|
192
|
+
applyEscapeProps(v, props);
|
|
193
|
+
if (v) binding.update(null, v, update);
|
|
194
|
+
},
|
|
195
|
+
props.decoding ?? "async",
|
|
196
|
+
props.alt
|
|
197
|
+
]);
|
|
72
198
|
}, () => {
|
|
73
|
-
return universalValue(
|
|
199
|
+
return universalValue(__octaneUniversalPlan2, [universalProps([
|
|
74
200
|
[
|
|
75
201
|
"set",
|
|
76
202
|
"ref",
|
|
77
|
-
(v) =>
|
|
203
|
+
(v) => {
|
|
204
|
+
imageRef.current = v;
|
|
205
|
+
applyEscapeProps(v, props);
|
|
206
|
+
if (v) binding.update(v, v, update);
|
|
207
|
+
}
|
|
78
208
|
],
|
|
79
209
|
[
|
|
80
210
|
"set",
|
|
@@ -94,8 +224,13 @@ var Image = defineUniversalComponent("nativescript", function Image(props) {
|
|
|
94
224
|
],
|
|
95
225
|
[
|
|
96
226
|
"set",
|
|
97
|
-
"
|
|
98
|
-
|
|
227
|
+
"stretch",
|
|
228
|
+
contentFitToStretch(fit)
|
|
229
|
+
],
|
|
230
|
+
[
|
|
231
|
+
"set",
|
|
232
|
+
"loadMode",
|
|
233
|
+
props.decoding ?? "async"
|
|
99
234
|
],
|
|
100
235
|
[
|
|
101
236
|
"set",
|
|
@@ -103,7 +238,7 @@ var Image = defineUniversalComponent("nativescript", function Image(props) {
|
|
|
103
238
|
props.alt
|
|
104
239
|
]
|
|
105
240
|
])]);
|
|
106
|
-
})]);
|
|
241
|
+
}))]);
|
|
107
242
|
}, { "module": "@nativescript-community/octane" });
|
|
108
243
|
//#endregion
|
|
109
244
|
export { Image };
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
//#region src/hash-image.ts
|
|
2
|
+
/** `blurhash:<hash>` / `thumbhash:<base64>` → PNG bytes as base64, or null. */
|
|
3
|
+
function hashToPngBase64(src) {
|
|
4
|
+
if (src.startsWith("blurhash:")) {
|
|
5
|
+
const rgba = decodeBlurHash(src.slice(9), 32, 32);
|
|
6
|
+
return rgba ? base64Encode(rgbaToPng(32, 32, rgba)) : null;
|
|
7
|
+
}
|
|
8
|
+
if (src.startsWith("thumbhash:")) {
|
|
9
|
+
const hash = base64Decode(src.slice(10));
|
|
10
|
+
const decoded = hash ? thumbHashToRGBA(hash) : null;
|
|
11
|
+
return decoded ? base64Encode(rgbaToPng(decoded.w, decoded.h, decoded.rgba)) : null;
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
var BASE83 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~";
|
|
16
|
+
function decode83(str, start, end) {
|
|
17
|
+
let value = 0;
|
|
18
|
+
for (let i = start; i < end; i++) {
|
|
19
|
+
const digit = BASE83.indexOf(str[i]);
|
|
20
|
+
if (digit === -1) return -1;
|
|
21
|
+
value = value * 83 + digit;
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
function srgbToLinear(value) {
|
|
26
|
+
const v = value / 255;
|
|
27
|
+
return v <= .04045 ? v / 12.92 : Math.pow((v + .055) / 1.055, 2.4);
|
|
28
|
+
}
|
|
29
|
+
function linearToSrgb(value) {
|
|
30
|
+
const v = Math.max(0, Math.min(1, value));
|
|
31
|
+
return v <= .0031308 ? Math.round(v * 12.92 * 255 + .5) : Math.round((1.055 * Math.pow(v, 1 / 2.4) - .055) * 255 + .5);
|
|
32
|
+
}
|
|
33
|
+
function signPow(value, exp) {
|
|
34
|
+
return Math.sign(value) * Math.pow(Math.abs(value), exp);
|
|
35
|
+
}
|
|
36
|
+
function decodeBlurHash(hash, width, height) {
|
|
37
|
+
if (hash.length < 6) return null;
|
|
38
|
+
const sizeFlag = decode83(hash, 0, 1);
|
|
39
|
+
const quantisedMax = decode83(hash, 1, 2);
|
|
40
|
+
const dcValue = decode83(hash, 2, 6);
|
|
41
|
+
if (sizeFlag < 0 || quantisedMax < 0 || dcValue < 0) return null;
|
|
42
|
+
const numY = Math.floor(sizeFlag / 9) + 1;
|
|
43
|
+
const numX = sizeFlag % 9 + 1;
|
|
44
|
+
if (hash.length !== 4 + 2 * numX * numY) return null;
|
|
45
|
+
const maximumValue = (quantisedMax + 1) / 166;
|
|
46
|
+
const colors = new Array(numX * numY);
|
|
47
|
+
colors[0] = [
|
|
48
|
+
srgbToLinear(dcValue >> 16),
|
|
49
|
+
srgbToLinear(dcValue >> 8 & 255),
|
|
50
|
+
srgbToLinear(dcValue & 255)
|
|
51
|
+
];
|
|
52
|
+
for (let i = 1; i < numX * numY; i++) {
|
|
53
|
+
const value = decode83(hash, 4 + i * 2, 6 + i * 2);
|
|
54
|
+
if (value < 0) return null;
|
|
55
|
+
const quantR = Math.floor(value / 361);
|
|
56
|
+
const quantG = Math.floor(value / 19) % 19;
|
|
57
|
+
const quantB = value % 19;
|
|
58
|
+
colors[i] = [
|
|
59
|
+
signPow((quantR - 9) / 9, 2) * maximumValue,
|
|
60
|
+
signPow((quantG - 9) / 9, 2) * maximumValue,
|
|
61
|
+
signPow((quantB - 9) / 9, 2) * maximumValue
|
|
62
|
+
];
|
|
63
|
+
}
|
|
64
|
+
const pixels = new Uint8ClampedArray(width * height * 4);
|
|
65
|
+
for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
|
|
66
|
+
let r = 0;
|
|
67
|
+
let g = 0;
|
|
68
|
+
let b = 0;
|
|
69
|
+
for (let j = 0; j < numY; j++) for (let i = 0; i < numX; i++) {
|
|
70
|
+
const basis = Math.cos(Math.PI * x * i / width) * Math.cos(Math.PI * y * j / height);
|
|
71
|
+
const color = colors[i + j * numX];
|
|
72
|
+
r += color[0] * basis;
|
|
73
|
+
g += color[1] * basis;
|
|
74
|
+
b += color[2] * basis;
|
|
75
|
+
}
|
|
76
|
+
const o = (x + y * width) * 4;
|
|
77
|
+
pixels[o] = linearToSrgb(r);
|
|
78
|
+
pixels[o + 1] = linearToSrgb(g);
|
|
79
|
+
pixels[o + 2] = linearToSrgb(b);
|
|
80
|
+
pixels[o + 3] = 255;
|
|
81
|
+
}
|
|
82
|
+
return pixels;
|
|
83
|
+
}
|
|
84
|
+
function thumbHashToApproximateAspectRatio(hash) {
|
|
85
|
+
const header = hash[3];
|
|
86
|
+
const hasAlpha = (hash[2] & 128) !== 0;
|
|
87
|
+
const isLandscape = (hash[4] & 128) !== 0;
|
|
88
|
+
return (isLandscape ? hasAlpha ? 5 : 7 : header & 7) / (isLandscape ? header & 7 : hasAlpha ? 5 : 7);
|
|
89
|
+
}
|
|
90
|
+
function thumbHashToRGBA(hash) {
|
|
91
|
+
if (hash.length < 5) return null;
|
|
92
|
+
const { PI, min, max, cos, round } = Math;
|
|
93
|
+
const header24 = hash[0] | hash[1] << 8 | hash[2] << 16;
|
|
94
|
+
const header16 = hash[3] | hash[4] << 8;
|
|
95
|
+
const l_dc = (header24 & 63) / 63;
|
|
96
|
+
const p_dc = (header24 >> 6 & 63) / 31.5 - 1;
|
|
97
|
+
const q_dc = (header24 >> 12 & 63) / 31.5 - 1;
|
|
98
|
+
const l_scale = (header24 >> 18 & 31) / 31;
|
|
99
|
+
const hasAlpha = header24 >> 23 !== 0;
|
|
100
|
+
const p_scale = (header16 >> 3 & 63) / 63;
|
|
101
|
+
const q_scale = (header16 >> 9 & 63) / 63;
|
|
102
|
+
const isLandscape = header16 >> 15 !== 0;
|
|
103
|
+
const lx = max(3, isLandscape ? hasAlpha ? 5 : 7 : header16 & 7);
|
|
104
|
+
const ly = max(3, isLandscape ? header16 & 7 : hasAlpha ? 5 : 7);
|
|
105
|
+
const a_dc = hasAlpha ? (hash[5] & 15) / 15 : 1;
|
|
106
|
+
const a_scale = (hash[5] >> 4) / 15;
|
|
107
|
+
const ac_start = hasAlpha ? 6 : 5;
|
|
108
|
+
let ac_index = 0;
|
|
109
|
+
const decodeChannel = (nx, ny, scale) => {
|
|
110
|
+
const ac = [];
|
|
111
|
+
for (let cy = 0; cy < ny; cy++) for (let cx = cy ? 0 : 1; cx * ny < nx * (ny - cy); cx++) {
|
|
112
|
+
const byte = hash[ac_start + (ac_index >> 1)];
|
|
113
|
+
if (byte === void 0) return null;
|
|
114
|
+
ac.push(((byte >> ((ac_index++ & 1) << 2) & 15) / 7.5 - 1) * scale);
|
|
115
|
+
}
|
|
116
|
+
return ac;
|
|
117
|
+
};
|
|
118
|
+
const l_ac = decodeChannel(lx, ly, l_scale);
|
|
119
|
+
const p_ac = decodeChannel(3, 3, p_scale * 1.25);
|
|
120
|
+
const q_ac = decodeChannel(3, 3, q_scale * 1.25);
|
|
121
|
+
const a_ac = hasAlpha ? decodeChannel(5, 5, a_scale) : null;
|
|
122
|
+
if (!l_ac || !p_ac || !q_ac || hasAlpha && !a_ac) return null;
|
|
123
|
+
const ratio = thumbHashToApproximateAspectRatio(hash);
|
|
124
|
+
const w = round(ratio > 1 ? 32 : 32 * ratio);
|
|
125
|
+
const h = round(ratio > 1 ? 32 / ratio : 32);
|
|
126
|
+
const rgba = new Uint8Array(w * h * 4);
|
|
127
|
+
const fx = [];
|
|
128
|
+
const fy = [];
|
|
129
|
+
for (let y = 0, i = 0; y < h; y++) for (let x = 0; x < w; x++, i += 4) {
|
|
130
|
+
let l = l_dc;
|
|
131
|
+
let p = p_dc;
|
|
132
|
+
let q = q_dc;
|
|
133
|
+
let a = a_dc;
|
|
134
|
+
for (let cx = 0, n = max(lx, hasAlpha ? 5 : 3); cx < n; cx++) fx[cx] = cos(PI / w * (x + .5) * cx);
|
|
135
|
+
for (let cy = 0, n = max(ly, hasAlpha ? 5 : 3); cy < n; cy++) fy[cy] = cos(PI / h * (y + .5) * cy);
|
|
136
|
+
for (let cy = 0, j = 0; cy < ly; cy++) for (let cx = cy ? 0 : 1, fy2 = fy[cy] * 2; cx * ly < lx * (ly - cy); cx++, j++) l += l_ac[j] * fx[cx] * fy2;
|
|
137
|
+
for (let cy = 0, j = 0; cy < 3; cy++) for (let cx = cy ? 0 : 1, fy2 = fy[cy] * 2; cx < 3 - cy; cx++, j++) {
|
|
138
|
+
const f = fx[cx] * fy2;
|
|
139
|
+
p += p_ac[j] * f;
|
|
140
|
+
q += q_ac[j] * f;
|
|
141
|
+
}
|
|
142
|
+
if (hasAlpha && a_ac) for (let cy = 0, j = 0; cy < 5; cy++) for (let cx = cy ? 0 : 1, fy2 = fy[cy] * 2; cx < 5 - cy; cx++, j++) a += a_ac[j] * fx[cx] * fy2;
|
|
143
|
+
const b = l - 2 / 3 * p;
|
|
144
|
+
const r = (3 * l - b + q) / 2;
|
|
145
|
+
const g = r - q;
|
|
146
|
+
rgba[i] = max(0, 255 * min(1, r));
|
|
147
|
+
rgba[i + 1] = max(0, 255 * min(1, g));
|
|
148
|
+
rgba[i + 2] = max(0, 255 * min(1, b));
|
|
149
|
+
rgba[i + 3] = max(0, 255 * min(1, a));
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
w,
|
|
153
|
+
h,
|
|
154
|
+
rgba
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
var PNG_CRC_TABLE = [
|
|
158
|
+
0,
|
|
159
|
+
498536548,
|
|
160
|
+
997073096,
|
|
161
|
+
651767980,
|
|
162
|
+
1994146192,
|
|
163
|
+
1802195444,
|
|
164
|
+
1303535960,
|
|
165
|
+
1342533948,
|
|
166
|
+
-306674912,
|
|
167
|
+
-267414716,
|
|
168
|
+
-690576408,
|
|
169
|
+
-882789492,
|
|
170
|
+
-1687895376,
|
|
171
|
+
-2032938284,
|
|
172
|
+
-1609899400,
|
|
173
|
+
-1111625188
|
|
174
|
+
];
|
|
175
|
+
/** Uncompressed PNG (zlib store blocks) — correctness over size; inputs are
|
|
176
|
+
* ~32px placeholders so the payload stays small. */
|
|
177
|
+
function rgbaToPng(w, h, rgba) {
|
|
178
|
+
const row = w * 4 + 1;
|
|
179
|
+
const idat = 6 + h * (5 + row);
|
|
180
|
+
const bytes = [
|
|
181
|
+
137,
|
|
182
|
+
80,
|
|
183
|
+
78,
|
|
184
|
+
71,
|
|
185
|
+
13,
|
|
186
|
+
10,
|
|
187
|
+
26,
|
|
188
|
+
10,
|
|
189
|
+
0,
|
|
190
|
+
0,
|
|
191
|
+
0,
|
|
192
|
+
13,
|
|
193
|
+
73,
|
|
194
|
+
72,
|
|
195
|
+
68,
|
|
196
|
+
82,
|
|
197
|
+
0,
|
|
198
|
+
0,
|
|
199
|
+
w >> 8,
|
|
200
|
+
w & 255,
|
|
201
|
+
0,
|
|
202
|
+
0,
|
|
203
|
+
h >> 8,
|
|
204
|
+
h & 255,
|
|
205
|
+
8,
|
|
206
|
+
6,
|
|
207
|
+
0,
|
|
208
|
+
0,
|
|
209
|
+
0,
|
|
210
|
+
0,
|
|
211
|
+
0,
|
|
212
|
+
0,
|
|
213
|
+
0,
|
|
214
|
+
idat >>> 24,
|
|
215
|
+
idat >> 16 & 255,
|
|
216
|
+
idat >> 8 & 255,
|
|
217
|
+
idat & 255,
|
|
218
|
+
73,
|
|
219
|
+
68,
|
|
220
|
+
65,
|
|
221
|
+
84,
|
|
222
|
+
120,
|
|
223
|
+
1
|
|
224
|
+
];
|
|
225
|
+
let a = 1;
|
|
226
|
+
let b = 0;
|
|
227
|
+
for (let y = 0, i = 0, end = row - 1; y < h; y++, end += row - 1) {
|
|
228
|
+
bytes.push(y + 1 < h ? 0 : 1, row & 255, row >> 8, ~row & 255, row >> 8 ^ 255, 0);
|
|
229
|
+
for (b = (b + a) % 65521; i < end; i++) {
|
|
230
|
+
const u = rgba[i] & 255;
|
|
231
|
+
bytes.push(u);
|
|
232
|
+
a = (a + u) % 65521;
|
|
233
|
+
b = (b + a) % 65521;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
bytes.push(b >> 8, b & 255, a >> 8, a & 255, 0, 0, 0, 0, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130);
|
|
237
|
+
for (let [start, end] of [[12, 29], [37, 41 + idat]]) {
|
|
238
|
+
let c = -1;
|
|
239
|
+
for (let i = start; i < end; i++) {
|
|
240
|
+
c ^= bytes[i];
|
|
241
|
+
c = c >>> 4 ^ PNG_CRC_TABLE[c & 15];
|
|
242
|
+
c = c >>> 4 ^ PNG_CRC_TABLE[c & 15];
|
|
243
|
+
}
|
|
244
|
+
c = ~c;
|
|
245
|
+
bytes[end++] = c >>> 24;
|
|
246
|
+
bytes[end++] = c >> 16 & 255;
|
|
247
|
+
bytes[end++] = c >> 8 & 255;
|
|
248
|
+
bytes[end++] = c & 255;
|
|
249
|
+
}
|
|
250
|
+
return bytes;
|
|
251
|
+
}
|
|
252
|
+
var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
253
|
+
function base64Encode(bytes) {
|
|
254
|
+
let out = "";
|
|
255
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
256
|
+
const b0 = bytes[i];
|
|
257
|
+
const b1 = i + 1 < bytes.length ? bytes[i + 1] : 0;
|
|
258
|
+
const b2 = i + 2 < bytes.length ? bytes[i + 2] : 0;
|
|
259
|
+
out += BASE64_CHARS[b0 >> 2];
|
|
260
|
+
out += BASE64_CHARS[(b0 & 3) << 4 | b1 >> 4];
|
|
261
|
+
out += i + 1 < bytes.length ? BASE64_CHARS[(b1 & 15) << 2 | b2 >> 6] : "=";
|
|
262
|
+
out += i + 2 < bytes.length ? BASE64_CHARS[b2 & 63] : "=";
|
|
263
|
+
}
|
|
264
|
+
return out;
|
|
265
|
+
}
|
|
266
|
+
function base64Decode(text) {
|
|
267
|
+
const clean = text.replace(/-/g, "+").replace(/_/g, "/").replace(/[\s=]/g, "");
|
|
268
|
+
const bytes = new Uint8Array(Math.floor(clean.length * 3 / 4));
|
|
269
|
+
let out = 0;
|
|
270
|
+
let acc = 0;
|
|
271
|
+
let bits = 0;
|
|
272
|
+
for (const ch of clean) {
|
|
273
|
+
const digit = BASE64_CHARS.indexOf(ch);
|
|
274
|
+
if (digit === -1) return null;
|
|
275
|
+
acc = acc << 6 | digit;
|
|
276
|
+
bits += 6;
|
|
277
|
+
if (bits >= 8) {
|
|
278
|
+
bits -= 8;
|
|
279
|
+
if (out < bytes.length) bytes[out++] = acc >> bits & 255;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return bytes.subarray(0, out);
|
|
283
|
+
}
|
|
284
|
+
//#endregion
|
|
285
|
+
export { hashToPngBase64 };
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { bestImageSource, contentRect } from "./image-content.js";
|
|
2
|
+
//#region src/image-content-binding.ts
|
|
3
|
+
/** Intrinsic size in dips. iOS `UIImage.size` is already points; Android
|
|
4
|
+
* `Bitmap` reports raw pixels — density-bucketed resource bitmaps convert
|
|
5
|
+
* through their bucket (a 300px xxxhdpi asset = 75 dips), while decoded
|
|
6
|
+
* remote/file bitmaps carry no density and treat px as dips, which is the
|
|
7
|
+
* CSS 'none' natural size on every platform. */
|
|
8
|
+
function intrinsicDipSize(imageSource, android) {
|
|
9
|
+
const width = imageSource?.width;
|
|
10
|
+
const height = imageSource?.height;
|
|
11
|
+
if (!width || !height) return null;
|
|
12
|
+
if (android) {
|
|
13
|
+
const density = imageSource.android?.getDensity?.() ?? 0;
|
|
14
|
+
if (density > 0) return {
|
|
15
|
+
w: width * 160 / density,
|
|
16
|
+
h: height * 160 / density
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
w: width,
|
|
21
|
+
h: height
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Measure → compute → apply for Image's content contract (study L7).
|
|
25
|
+
* NS's stock widget owns its draw path — the widgets ImageView computes
|
|
26
|
+
* its own matrix in onDraw and honors only the four stretch scaleTypes —
|
|
27
|
+
* so the drawable-level matrix expo-image uses is engine internals. What
|
|
28
|
+
* the leaf can own is the view-level equivalent: measure the box
|
|
29
|
+
* (`layoutChanged` → `getActualSize`, dips), compute the content rect
|
|
30
|
+
* with the same math as object-fit/object-position, and lay out the
|
|
31
|
+
* inner <image stretch="fill"> to that rect inside a clipped host. One
|
|
32
|
+
* code path produces identical rects on iOS and Android.
|
|
33
|
+
*
|
|
34
|
+
* This binding never writes `src` itself — it resolves WHICH source the
|
|
35
|
+
* view should load and hands it to `env.issue`, the sized-decode binding
|
|
36
|
+
* (study L1). That seam composes both contracts: the L8 array pick waits
|
|
37
|
+
* on the same first real layout the decode gate needs, and the computed
|
|
38
|
+
* path passes the semantic fit as the decode gate so 'none' still
|
|
39
|
+
* decodes at source resolution while 'fill' would have been
|
|
40
|
+
* view-sampled.
|
|
41
|
+
*
|
|
42
|
+
* Before the bitmap's intrinsic size is known the computed path sizes
|
|
43
|
+
* the inner image to the host box: a real rect is what lets the decode
|
|
44
|
+
* gate's first-layout check pass, and the placeholder is the CSS 'fill'
|
|
45
|
+
* answer until imageSourceChange refines it.
|
|
46
|
+
*
|
|
47
|
+
* `update` re-runs on every render (ref identity churn); all writes are
|
|
48
|
+
* idempotent. Null refs on detach are ignored — a dead listener is a
|
|
49
|
+
* self-contained cycle GC collects with the view. */
|
|
50
|
+
function createImageContentBinding(env) {
|
|
51
|
+
let sizeView = null;
|
|
52
|
+
let imageView = null;
|
|
53
|
+
let update = {
|
|
54
|
+
source: null,
|
|
55
|
+
sources: null,
|
|
56
|
+
fit: "cover",
|
|
57
|
+
position: {},
|
|
58
|
+
computed: false
|
|
59
|
+
};
|
|
60
|
+
let appliedW = 0;
|
|
61
|
+
let appliedH = 0;
|
|
62
|
+
let appliedX = 0;
|
|
63
|
+
let appliedY = 0;
|
|
64
|
+
let listening = false;
|
|
65
|
+
let sourceListening = false;
|
|
66
|
+
const screenScale = () => {
|
|
67
|
+
try {
|
|
68
|
+
return env.screenScale();
|
|
69
|
+
} catch {
|
|
70
|
+
return 1;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const decodeGate = () => update.fit === "none" ? "none" : "aspectFit";
|
|
74
|
+
const evaluate = () => {
|
|
75
|
+
if (!sizeView) return;
|
|
76
|
+
const needsViewSize = update.computed || update.sources != null;
|
|
77
|
+
let viewWidth = 0;
|
|
78
|
+
let viewHeight = 0;
|
|
79
|
+
if (needsViewSize) {
|
|
80
|
+
const actual = sizeView.getActualSize?.();
|
|
81
|
+
viewWidth = actual?.width ?? 0;
|
|
82
|
+
viewHeight = actual?.height ?? 0;
|
|
83
|
+
if (viewWidth <= 0 || viewHeight <= 0) return;
|
|
84
|
+
}
|
|
85
|
+
if (imageView) {
|
|
86
|
+
let resolved = update.source;
|
|
87
|
+
if (update.sources?.length) resolved = bestImageSource(update.sources, viewWidth, viewHeight, screenScale())?.uri;
|
|
88
|
+
if (resolved != null && resolved !== "") env.issue(imageView, resolved, update.computed ? decodeGate() : void 0);
|
|
89
|
+
}
|
|
90
|
+
if (!update.computed || !imageView) return;
|
|
91
|
+
const intrinsic = intrinsicDipSize(imageView.imageSource, env.isAndroid);
|
|
92
|
+
const rect = intrinsic ? contentRect(update.fit, update.position, viewWidth, viewHeight, intrinsic.w, intrinsic.h) : {
|
|
93
|
+
x: 0,
|
|
94
|
+
y: 0,
|
|
95
|
+
width: viewWidth,
|
|
96
|
+
height: viewHeight
|
|
97
|
+
};
|
|
98
|
+
if (rect.width !== appliedW || rect.height !== appliedH || rect.x !== appliedX || rect.y !== appliedY) {
|
|
99
|
+
appliedW = rect.width;
|
|
100
|
+
appliedH = rect.height;
|
|
101
|
+
appliedX = rect.x;
|
|
102
|
+
appliedY = rect.y;
|
|
103
|
+
imageView.width = rect.width;
|
|
104
|
+
imageView.height = rect.height;
|
|
105
|
+
imageView.left = rect.x;
|
|
106
|
+
imageView.top = rect.y;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
const onLayout = () => evaluate();
|
|
110
|
+
const onImageSource = () => evaluate();
|
|
111
|
+
const listen = () => {
|
|
112
|
+
const wantsMeasure = update.computed || update.sources != null;
|
|
113
|
+
if (wantsMeasure && !listening && typeof sizeView?.on === "function") {
|
|
114
|
+
sizeView.on("layoutChanged", onLayout);
|
|
115
|
+
listening = true;
|
|
116
|
+
} else if (!wantsMeasure && listening) {
|
|
117
|
+
sizeView?.off?.("layoutChanged", onLayout);
|
|
118
|
+
listening = false;
|
|
119
|
+
}
|
|
120
|
+
if (update.computed && !sourceListening && typeof imageView?.on === "function") {
|
|
121
|
+
imageView.on("imageSourceChange", onImageSource);
|
|
122
|
+
sourceListening = true;
|
|
123
|
+
} else if (!update.computed && sourceListening) {
|
|
124
|
+
imageView?.off?.("imageSourceChange", onImageSource);
|
|
125
|
+
sourceListening = false;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
return {
|
|
129
|
+
/** (Re)bind views + intent. `sizeView` is the measured box (the image
|
|
130
|
+
* itself on the stretch path, the clip host on the computed path);
|
|
131
|
+
* `imageView` is the <image> that draws. */
|
|
132
|
+
update(nextSizeView, nextImageView, next) {
|
|
133
|
+
if (nextSizeView && nextSizeView !== sizeView) {
|
|
134
|
+
sizeView?.off?.("layoutChanged", onLayout);
|
|
135
|
+
sizeView = nextSizeView;
|
|
136
|
+
listening = false;
|
|
137
|
+
}
|
|
138
|
+
if (nextImageView && nextImageView !== imageView) {
|
|
139
|
+
imageView?.off?.("imageSourceChange", onImageSource);
|
|
140
|
+
imageView = nextImageView;
|
|
141
|
+
sourceListening = false;
|
|
142
|
+
}
|
|
143
|
+
update = next;
|
|
144
|
+
listen();
|
|
145
|
+
evaluate();
|
|
146
|
+
},
|
|
147
|
+
dispose() {
|
|
148
|
+
sizeView?.off?.("layoutChanged", onLayout);
|
|
149
|
+
imageView?.off?.("imageSourceChange", onImageSource);
|
|
150
|
+
sizeView = null;
|
|
151
|
+
imageView = null;
|
|
152
|
+
listening = false;
|
|
153
|
+
sourceListening = false;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
export { createImageContentBinding };
|