@revenuecat/purchases-ui-js 4.12.4 → 4.13.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/components/icon/Icon.svelte +2 -0
- package/dist/components/image/Image.svelte +14 -3
- package/dist/components/input-text/InputText.svelte +2 -0
- package/dist/components/loader-animation/LoaderAnimation.stories.svelte +42 -0
- package/dist/components/loader-animation/LoaderAnimation.svelte +12 -1
- package/dist/components/loader-group/LoaderGroup.svelte +2 -12
- package/dist/components/loader-progress-components/LoaderProgressComponents.svelte +7 -1
- package/dist/components/paywall/Paywall.svelte +7 -2
- package/dist/components/paywall/Sheet.svelte +2 -1
- package/dist/components/skeleton-loader/SkeletonLoader.svelte +2 -0
- package/dist/components/stack/Stack.stories.svelte +77 -0
- package/dist/components/stack/Stack.svelte +2 -14
- package/dist/components/stack/stack-utils.d.ts +3 -1
- package/dist/components/stack/stack-utils.js +13 -1
- package/dist/components/tabs/Tabs.svelte +2 -0
- package/dist/components/text/text-utils.js +2 -1
- package/dist/components/timeline/timeline-utils.d.ts +2 -2
- package/dist/components/timeline/timeline-utils.js +2 -1
- package/dist/components/video/Video.stories.svelte +29 -1
- package/dist/components/video/Video.svelte +24 -4
- package/dist/components/wallet-button/WalletButton.svelte +8 -1
- package/dist/components/web-view/WebView.svelte +2 -1
- package/dist/stores/localization.d.ts +5 -2
- package/dist/stores/localization.js +8 -6
- package/dist/types/components/video.d.ts +1 -1
- package/dist/types/localization.d.ts +2 -1
- package/dist/types/paywall.d.ts +2 -1
- package/dist/types.d.ts +3 -0
- package/dist/utils/base-utils.d.ts +5 -1
- package/dist/utils/base-utils.js +24 -0
- package/dist/utils/source-utils.d.ts +16 -0
- package/dist/utils/source-utils.js +19 -0
- package/dist/utils/style-utils.d.ts +1 -1
- package/package.json +1 -1
- package/dist/components/image/image-utils.d.ts +0 -9
- package/dist/components/image/image-utils.js +0 -20
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
mapBorder,
|
|
8
8
|
mapBorderRadius,
|
|
9
9
|
mapColor,
|
|
10
|
+
mapFillBounds,
|
|
10
11
|
mapShadow,
|
|
11
12
|
mapSize,
|
|
12
13
|
mapSpacing,
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
css({
|
|
39
40
|
width: mapSize(size.width),
|
|
40
41
|
height: mapSize(size.height),
|
|
42
|
+
...mapFillBounds(size),
|
|
41
43
|
margin: mapSpacing(margin),
|
|
42
44
|
padding: mapSpacing(padding),
|
|
43
45
|
"background-color": mapColor(colorMode, background?.color),
|
|
@@ -4,16 +4,18 @@
|
|
|
4
4
|
import { getLocalizationContext } from "../../stores/localization";
|
|
5
5
|
import type { ImageProps } from "../../types/components/image";
|
|
6
6
|
import {
|
|
7
|
+
clampFillSize,
|
|
7
8
|
css,
|
|
8
9
|
mapColor,
|
|
9
10
|
mapColorMode,
|
|
11
|
+
mapFillBounds,
|
|
10
12
|
mapFitMode,
|
|
11
13
|
mapSize,
|
|
12
14
|
mapSpacing,
|
|
13
15
|
} from "../../utils/base-utils";
|
|
14
16
|
import { resolveOverrideProperties } from "../../utils/style-utils";
|
|
17
|
+
import { resolveLocalizedSource } from "../../utils/source-utils";
|
|
15
18
|
import ClipPath from "./ClipPath.svelte";
|
|
16
|
-
import { resolveImageSource } from "./image-utils";
|
|
17
19
|
import { viewBoxSize } from "./view-box-size";
|
|
18
20
|
import Overlay from "./Overlay.svelte";
|
|
19
21
|
|
|
@@ -43,7 +45,12 @@
|
|
|
43
45
|
});
|
|
44
46
|
|
|
45
47
|
const source = $derived(
|
|
46
|
-
|
|
48
|
+
resolveLocalizedSource(
|
|
49
|
+
props,
|
|
50
|
+
"override_source_lid",
|
|
51
|
+
conditionContext,
|
|
52
|
+
getLocalizedImage,
|
|
53
|
+
),
|
|
47
54
|
);
|
|
48
55
|
|
|
49
56
|
const getColorMode = getColorModeContext();
|
|
@@ -86,7 +93,10 @@
|
|
|
86
93
|
if (size.height.type === "fixed") {
|
|
87
94
|
return size.height.value;
|
|
88
95
|
}
|
|
89
|
-
|
|
96
|
+
const aspectHeight = Math.round(
|
|
97
|
+
wrapperWidth * (imageSize.height / imageSize.width),
|
|
98
|
+
);
|
|
99
|
+
return clampFillSize(aspectHeight, size.height);
|
|
90
100
|
});
|
|
91
101
|
|
|
92
102
|
const style = $derived(
|
|
@@ -102,6 +112,7 @@
|
|
|
102
112
|
...(size.width.type === "fixed"
|
|
103
113
|
? {}
|
|
104
114
|
: { "min-width": 0, "min-height": 0 }),
|
|
115
|
+
...mapFillBounds(size),
|
|
105
116
|
}),
|
|
106
117
|
);
|
|
107
118
|
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
css,
|
|
17
17
|
mapBorder,
|
|
18
18
|
mapBorderRadius,
|
|
19
|
+
mapFillBounds,
|
|
19
20
|
mapShadow,
|
|
20
21
|
mapSize,
|
|
21
22
|
mapSpacing,
|
|
@@ -221,6 +222,7 @@
|
|
|
221
222
|
"flex-direction": "column",
|
|
222
223
|
width: mapSize(size.width),
|
|
223
224
|
...(heightOnContainer ? { height: mapSize(size.height) } : {}),
|
|
225
|
+
...mapFillBounds(size),
|
|
224
226
|
margin: mapSpacing(margin),
|
|
225
227
|
}),
|
|
226
228
|
);
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
import { setLoaderContext } from "../../stores/loader";
|
|
6
6
|
import type { LoaderAnimationProps } from "../../types/components/loader-animation";
|
|
7
7
|
import { defineMeta } from "@storybook/addon-svelte-csf";
|
|
8
|
+
import type { ComponentProps } from "svelte";
|
|
8
9
|
import { writable } from "svelte/store";
|
|
10
|
+
import { expect } from "storybook/test";
|
|
9
11
|
import type { DecoratorFunction, Renderer } from "storybook/internal/csf";
|
|
10
12
|
|
|
11
13
|
/**
|
|
@@ -64,6 +66,12 @@
|
|
|
64
66
|
});
|
|
65
67
|
</script>
|
|
66
68
|
|
|
69
|
+
{#snippet linearClampTemplate(props: ComponentProps<typeof LoaderAnimation>)}
|
|
70
|
+
<div class="linear-clamp-stage">
|
|
71
|
+
<LoaderAnimation {...props} />
|
|
72
|
+
</div>
|
|
73
|
+
{/snippet}
|
|
74
|
+
|
|
67
75
|
<Story
|
|
68
76
|
name="Circular — In progress"
|
|
69
77
|
decorators={[staticLoaderDecorator(0.7)]}
|
|
@@ -94,3 +102,37 @@
|
|
|
94
102
|
decorators={[staticLoaderDecorator(1)]}
|
|
95
103
|
args={linearArgs}
|
|
96
104
|
/>
|
|
105
|
+
|
|
106
|
+
<Story
|
|
107
|
+
name="Linear — Clamped fill height"
|
|
108
|
+
decorators={[staticLoaderDecorator(0.5)]}
|
|
109
|
+
template={linearClampTemplate}
|
|
110
|
+
play={async ({ canvasElement }) => {
|
|
111
|
+
const loader = canvasElement.querySelector(".rc-loader-animation--linear");
|
|
112
|
+
await expect(loader?.getBoundingClientRect().height).toBe(48);
|
|
113
|
+
}}
|
|
114
|
+
args={{
|
|
115
|
+
...linearArgs,
|
|
116
|
+
track: {
|
|
117
|
+
...linearArgs.track,
|
|
118
|
+
size: {
|
|
119
|
+
width: { type: "fill", min: 200, max: 320 },
|
|
120
|
+
height: { type: "fill", min: 24, max: 48 },
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
fill: {
|
|
124
|
+
...linearArgs.fill,
|
|
125
|
+
size: {
|
|
126
|
+
width: { type: "fill", min: 200, max: 320 },
|
|
127
|
+
height: { type: "fill", min: 24, max: 48 },
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
}}
|
|
131
|
+
/>
|
|
132
|
+
|
|
133
|
+
<style>
|
|
134
|
+
.linear-clamp-stage {
|
|
135
|
+
width: 400px;
|
|
136
|
+
height: 80px;
|
|
137
|
+
}
|
|
138
|
+
</style>
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
mapColor,
|
|
21
21
|
mapColorInfo,
|
|
22
22
|
mapColorMode,
|
|
23
|
+
mapFillBounds,
|
|
23
24
|
mapShadow,
|
|
24
25
|
mapSize,
|
|
25
26
|
mapSpacing,
|
|
@@ -71,8 +72,12 @@
|
|
|
71
72
|
const pillWidth = (layer: LinearAnimationLayer) =>
|
|
72
73
|
layer.size.width.type === "fill" ? "100%" : mapSize(layer.size.width);
|
|
73
74
|
|
|
75
|
+
const pillHeight = (layer: LinearAnimationLayer) =>
|
|
76
|
+
layer.size.height.type === "fill" ? "100%" : mapSize(layer.size.height);
|
|
77
|
+
|
|
74
78
|
const pillLayerStyle = (layer: LinearAnimationLayer) => ({
|
|
75
|
-
height:
|
|
79
|
+
height: pillHeight(layer),
|
|
80
|
+
...mapFillBounds({ height: layer.size.height }),
|
|
76
81
|
"border-radius": mapBorderRadius(layer.shape ?? null),
|
|
77
82
|
background: mapColor(colorMode, layer.color),
|
|
78
83
|
...mapBorder(colorMode, layer.border ?? null),
|
|
@@ -119,6 +124,7 @@
|
|
|
119
124
|
"aspect-ratio": "1",
|
|
120
125
|
"flex-shrink": "0",
|
|
121
126
|
width: diameterWidth(actualProps.track),
|
|
127
|
+
...mapFillBounds({ width: actualProps.track.diameter }),
|
|
122
128
|
margin: mapSpacing(actualProps.margin ?? null),
|
|
123
129
|
padding: mapSpacing(actualProps.padding ?? null),
|
|
124
130
|
})}
|
|
@@ -169,6 +175,11 @@
|
|
|
169
175
|
display: "block",
|
|
170
176
|
"flex-shrink": "0",
|
|
171
177
|
width: pillWidth(actualProps.track),
|
|
178
|
+
...(actualProps.track.size.height.type === "fill"
|
|
179
|
+
? { height: "100%" }
|
|
180
|
+
: {}),
|
|
181
|
+
...mapFillBounds({ width: actualProps.track.size.width }),
|
|
182
|
+
...mapFillBounds({ height: actualProps.track.size.height }),
|
|
172
183
|
margin: mapSpacing(actualProps.margin ?? null),
|
|
173
184
|
padding: mapSpacing(actualProps.padding ?? null),
|
|
174
185
|
})}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import {
|
|
3
3
|
mapDimension,
|
|
4
|
-
mapFillHeight,
|
|
5
|
-
mapFixedHeightBounds,
|
|
6
|
-
mapFixedWidthBounds,
|
|
7
4
|
mapLayerAlignment,
|
|
5
|
+
mapStackSize,
|
|
8
6
|
} from "../stack/stack-utils";
|
|
9
7
|
import { setStackDimensionContext } from "../stack/stack-dimension-context";
|
|
10
8
|
import BackgroundVideoSurface from "../paywall/BackgroundVideoSurface.svelte";
|
|
@@ -21,7 +19,6 @@
|
|
|
21
19
|
mapBorder,
|
|
22
20
|
mapBorderRadius,
|
|
23
21
|
mapShadow,
|
|
24
|
-
mapSize,
|
|
25
22
|
mapSpacing,
|
|
26
23
|
px,
|
|
27
24
|
} from "../../utils/base-utils";
|
|
@@ -59,18 +56,11 @@
|
|
|
59
56
|
});
|
|
60
57
|
|
|
61
58
|
const borderRadiusCss = $derived(mapBorderRadius(actualProps.shape));
|
|
62
|
-
const heightFill = $derived(actualProps.size.height.type === "fill");
|
|
63
|
-
|
|
64
59
|
const containerStyle = $derived(
|
|
65
60
|
css({
|
|
66
61
|
display: "flex",
|
|
67
62
|
position: "relative",
|
|
68
|
-
|
|
69
|
-
...(heightFill
|
|
70
|
-
? mapFillHeight(getParentStackDimension?.())
|
|
71
|
-
: { height: mapSize(actualProps.size.height) }),
|
|
72
|
-
...mapFixedWidthBounds(actualProps.size.width),
|
|
73
|
-
...mapFixedHeightBounds(actualProps.size.height),
|
|
63
|
+
...mapStackSize(actualProps.size, getParentStackDimension?.()),
|
|
74
64
|
...mapDimension(actualProps.dimension),
|
|
75
65
|
gap: px(actualProps.spacing),
|
|
76
66
|
margin: mapSpacing(actualProps.margin),
|
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
import { useConditionContext } from "../../stores/condition-context.svelte";
|
|
5
5
|
import { getRequiredLoaderContext } from "../../stores/loader";
|
|
6
6
|
import type { LoaderProgressComponentsProps } from "../../types/components/loader-progress-components";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
css,
|
|
9
|
+
mapFillBounds,
|
|
10
|
+
mapSize,
|
|
11
|
+
mapSpacing,
|
|
12
|
+
} from "../../utils/base-utils";
|
|
8
13
|
import { resolveOverrideProperties } from "../../utils/style-utils";
|
|
9
14
|
|
|
10
15
|
const props: LoaderProgressComponentsProps = $props();
|
|
@@ -49,6 +54,7 @@
|
|
|
49
54
|
"justify-content": "center",
|
|
50
55
|
width: mapSize(actualProps.size.width),
|
|
51
56
|
height: mapSize(actualProps.size.height),
|
|
57
|
+
...mapFillBounds(actualProps.size),
|
|
52
58
|
padding: mapSpacing(actualProps.padding),
|
|
53
59
|
margin: mapSpacing(actualProps.margin),
|
|
54
60
|
}),
|
|
@@ -224,8 +224,12 @@
|
|
|
224
224
|
}),
|
|
225
225
|
);
|
|
226
226
|
|
|
227
|
-
const {
|
|
228
|
-
|
|
227
|
+
const {
|
|
228
|
+
default_locale,
|
|
229
|
+
components_config,
|
|
230
|
+
components_localizations,
|
|
231
|
+
components_video_localizations,
|
|
232
|
+
} = paywallData;
|
|
229
233
|
const { base } = components_config;
|
|
230
234
|
const defaultPackageId = findSelectedPackageId(base);
|
|
231
235
|
|
|
@@ -250,6 +254,7 @@
|
|
|
250
254
|
defaultLocale: default_locale,
|
|
251
255
|
selectedLocale,
|
|
252
256
|
localizations: components_localizations!,
|
|
257
|
+
videoLocalizations: components_video_localizations,
|
|
253
258
|
}));
|
|
254
259
|
|
|
255
260
|
const selectedPackageId = writable<string | undefined>(defaultPackageId);
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
rcVideoBackgroundHostClass,
|
|
13
13
|
videoBackgroundHostStyles,
|
|
14
14
|
} from "../../utils/video-background-host";
|
|
15
|
-
import { css, mapSize } from "../../utils/base-utils";
|
|
15
|
+
import { css, mapFillBounds, mapSize } from "../../utils/base-utils";
|
|
16
16
|
import { resolveOverrideProperties } from "../../utils/style-utils";
|
|
17
17
|
import { onMount } from "svelte";
|
|
18
18
|
import Stack from "../stack/Stack.svelte";
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
const sheetStyle = $derived(
|
|
49
49
|
css({
|
|
50
50
|
height: mapSize(size.height),
|
|
51
|
+
...mapFillBounds({ height: size.height }),
|
|
51
52
|
...videoBackgroundHostStyles({
|
|
52
53
|
hasVideoBg,
|
|
53
54
|
clipOverflow: true,
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
css,
|
|
5
5
|
mapBorder,
|
|
6
6
|
mapBorderRadius,
|
|
7
|
+
mapFillBounds,
|
|
7
8
|
mapShadow,
|
|
8
9
|
mapSize,
|
|
9
10
|
mapSpacing,
|
|
@@ -51,6 +52,7 @@
|
|
|
51
52
|
position: "relative",
|
|
52
53
|
width: mapSize(size.width),
|
|
53
54
|
height: mapSize(size.height),
|
|
55
|
+
...mapFillBounds(size),
|
|
54
56
|
...mapDimension(dimension),
|
|
55
57
|
gap: px(spacing),
|
|
56
58
|
margin: mapSpacing({
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
import { STORY_BACKGROUND_VIDEO_FILL } from "../../stories/video-background-story-fixture";
|
|
11
11
|
import { DEFAULT_TEXT_COLOR } from "../../utils/constants";
|
|
12
12
|
import { defineMeta } from "@storybook/addon-svelte-csf";
|
|
13
|
+
import type { ComponentProps } from "svelte";
|
|
14
|
+
import { expect } from "storybook/test";
|
|
13
15
|
|
|
14
16
|
const defaultLocale = Object.keys(localizations)[0];
|
|
15
17
|
|
|
@@ -198,6 +200,23 @@
|
|
|
198
200
|
});
|
|
199
201
|
</script>
|
|
200
202
|
|
|
203
|
+
{#snippet fillClampTemplate(props: ComponentProps<typeof Stack>)}
|
|
204
|
+
<div class="fill-clamp-layouts">
|
|
205
|
+
<Stack {...props} testId="vertical-fill-clamp" />
|
|
206
|
+
<Stack
|
|
207
|
+
{...props}
|
|
208
|
+
id="horizontal-fill-clamp"
|
|
209
|
+
name="Horizontal fill clamp"
|
|
210
|
+
testId="horizontal-fill-clamp"
|
|
211
|
+
dimension={{
|
|
212
|
+
type: "horizontal",
|
|
213
|
+
alignment: "center",
|
|
214
|
+
distribution: "start",
|
|
215
|
+
}}
|
|
216
|
+
/>
|
|
217
|
+
</div>
|
|
218
|
+
{/snippet}
|
|
219
|
+
|
|
201
220
|
<Story name="Vertical Stack" />
|
|
202
221
|
|
|
203
222
|
<Story
|
|
@@ -211,6 +230,56 @@
|
|
|
211
230
|
}}
|
|
212
231
|
/>
|
|
213
232
|
|
|
233
|
+
<Story
|
|
234
|
+
name="Fill min/max clamps"
|
|
235
|
+
template={fillClampTemplate}
|
|
236
|
+
play={async ({ canvasElement }) => {
|
|
237
|
+
for (const testId of ["vertical-fill-clamp", "horizontal-fill-clamp"]) {
|
|
238
|
+
const child = canvasElement.querySelector(
|
|
239
|
+
`[data-testid="${testId}"] > .stack`,
|
|
240
|
+
);
|
|
241
|
+
const bounds = child?.getBoundingClientRect();
|
|
242
|
+
await expect(bounds?.width).toBe(220);
|
|
243
|
+
await expect(bounds?.height).toBe(140);
|
|
244
|
+
}
|
|
245
|
+
}}
|
|
246
|
+
args={{
|
|
247
|
+
size: {
|
|
248
|
+
width: { type: "fixed", value: 360 },
|
|
249
|
+
height: { type: "fixed", value: 240 },
|
|
250
|
+
},
|
|
251
|
+
components: [
|
|
252
|
+
{
|
|
253
|
+
type: "stack",
|
|
254
|
+
id: "clamped-child",
|
|
255
|
+
name: "Clamped child",
|
|
256
|
+
components: [],
|
|
257
|
+
size: {
|
|
258
|
+
width: { type: "fill", min: 120, max: 220 },
|
|
259
|
+
height: { type: "fill", min: 80, max: 140 },
|
|
260
|
+
},
|
|
261
|
+
dimension: {
|
|
262
|
+
type: "vertical",
|
|
263
|
+
alignment: "center",
|
|
264
|
+
distribution: "center",
|
|
265
|
+
},
|
|
266
|
+
spacing: 0,
|
|
267
|
+
margin: { top: 0, trailing: 0, bottom: 0, leading: 0 },
|
|
268
|
+
padding: { top: 0, trailing: 0, bottom: 0, leading: 0 },
|
|
269
|
+
background: {
|
|
270
|
+
type: "color",
|
|
271
|
+
value: { light: { type: "hex", value: "#6C63FF" } },
|
|
272
|
+
},
|
|
273
|
+
background_color: null,
|
|
274
|
+
border: null,
|
|
275
|
+
shape: null,
|
|
276
|
+
shadow: null,
|
|
277
|
+
badge: null,
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
}}
|
|
281
|
+
/>
|
|
282
|
+
|
|
214
283
|
<Story
|
|
215
284
|
name="Surface — video background"
|
|
216
285
|
parameters={{
|
|
@@ -3018,3 +3087,11 @@
|
|
|
3018
3087
|
},
|
|
3019
3088
|
}}
|
|
3020
3089
|
/>
|
|
3090
|
+
|
|
3091
|
+
<style>
|
|
3092
|
+
.fill-clamp-layouts {
|
|
3093
|
+
display: flex;
|
|
3094
|
+
flex-wrap: wrap;
|
|
3095
|
+
gap: 24px;
|
|
3096
|
+
}
|
|
3097
|
+
</style>
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
mapBorder,
|
|
18
18
|
mapBorderRadius,
|
|
19
19
|
mapShadow,
|
|
20
|
-
mapSize,
|
|
21
20
|
mapSpacing,
|
|
22
21
|
px,
|
|
23
22
|
type CSS,
|
|
@@ -27,10 +26,8 @@
|
|
|
27
26
|
import {
|
|
28
27
|
mapBadge,
|
|
29
28
|
mapDimension,
|
|
30
|
-
mapFillHeight,
|
|
31
|
-
mapFixedHeightBounds,
|
|
32
|
-
mapFixedWidthBounds,
|
|
33
29
|
mapLayerAlignment,
|
|
30
|
+
mapStackSize,
|
|
34
31
|
} from "./stack-utils";
|
|
35
32
|
import { setStackDimensionContext } from "./stack-dimension-context";
|
|
36
33
|
|
|
@@ -85,20 +82,11 @@
|
|
|
85
82
|
});
|
|
86
83
|
const borderRadiusCss = $derived(mapBorderRadius(shape));
|
|
87
84
|
|
|
88
|
-
const heightFill = $derived(size.height.type === "fill");
|
|
89
|
-
|
|
90
85
|
const stackStyle = $derived(
|
|
91
86
|
css({
|
|
92
87
|
display: "flex",
|
|
93
88
|
position: "relative",
|
|
94
|
-
|
|
95
|
-
// Fill height: definite height + growth in columns; stretch in rows
|
|
96
|
-
// (see mapFillHeight). Lets nested fill children (e.g. web_view) resolve.
|
|
97
|
-
...(heightFill
|
|
98
|
-
? mapFillHeight(getParentStackDimension?.())
|
|
99
|
-
: { height: mapSize(size.height) }),
|
|
100
|
-
...mapFixedWidthBounds(size.width),
|
|
101
|
-
...mapFixedHeightBounds(size.height),
|
|
89
|
+
...mapStackSize(size, getParentStackDimension?.()),
|
|
102
90
|
...mapDimension(dimension),
|
|
103
91
|
gap: px(spacing),
|
|
104
92
|
margin: mapSpacing(margin),
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type { Size } from "../../types";
|
|
1
|
+
import type { Size, SizeType } from "../../types";
|
|
2
2
|
import type { Dimension, ZAlignment } from "../../types/alignment";
|
|
3
3
|
import type { Badge } from "../../types/components/stack";
|
|
4
4
|
import { type CSS } from "../../utils/base-utils";
|
|
5
5
|
export declare function mapFixedWidthBounds(width: Size): CSS;
|
|
6
6
|
export declare function mapFixedHeightBounds(height: Size): CSS;
|
|
7
7
|
export declare function mapFillHeight(parentDimension?: Dimension["type"]): CSS;
|
|
8
|
+
/** Fill clamps must follow the flex min-size reset so authored bounds win. */
|
|
9
|
+
export declare function mapStackSize(size: SizeType, parentDimension?: Dimension["type"]): CSS;
|
|
8
10
|
export declare function mapDimension(dimension: Dimension): Record<string, string>;
|
|
9
11
|
type JustifyAlign = {
|
|
10
12
|
justifyContent: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { px } from "../../utils/base-utils";
|
|
1
|
+
import { mapFillBounds, mapSize, px } from "../../utils/base-utils";
|
|
2
2
|
const ALIGNMENT_MAP = Object.freeze({
|
|
3
3
|
leading: "flex-start",
|
|
4
4
|
top: "flex-start",
|
|
@@ -50,6 +50,18 @@ export function mapFillHeight(parentDimension) {
|
|
|
50
50
|
: {}),
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
+
/** Fill clamps must follow the flex min-size reset so authored bounds win. */
|
|
54
|
+
export function mapStackSize(size, parentDimension) {
|
|
55
|
+
return {
|
|
56
|
+
width: mapSize(size.width),
|
|
57
|
+
...(size.height.type === "fill"
|
|
58
|
+
? mapFillHeight(parentDimension)
|
|
59
|
+
: { height: mapSize(size.height) }),
|
|
60
|
+
...mapFixedWidthBounds(size.width),
|
|
61
|
+
...mapFixedHeightBounds(size.height),
|
|
62
|
+
...mapFillBounds(size),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
53
65
|
export function mapDimension(dimension) {
|
|
54
66
|
if (dimension.type === "zlayer") {
|
|
55
67
|
return {};
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
css,
|
|
16
16
|
mapBorder,
|
|
17
17
|
mapBorderRadius,
|
|
18
|
+
mapFillBounds,
|
|
18
19
|
mapShadow,
|
|
19
20
|
mapSize,
|
|
20
21
|
mapSpacing,
|
|
@@ -64,6 +65,7 @@
|
|
|
64
65
|
position: "relative",
|
|
65
66
|
width: mapSize(size.width),
|
|
66
67
|
height: mapSize(size.height),
|
|
68
|
+
...mapFillBounds(size),
|
|
67
69
|
margin: mapSpacing(margin),
|
|
68
70
|
padding: mapSpacing(padding),
|
|
69
71
|
...videoBackgroundHostStyles({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FontSizes, FontWeights, TextAlignments, } from "../../types";
|
|
2
2
|
import { mapBackground } from "../../utils/background-utils";
|
|
3
|
-
import { css, mapColorInfo, mapColorMode, mapSize, mapSpacing, } from "../../utils/base-utils";
|
|
3
|
+
import { css, mapColorInfo, mapColorMode, mapFillBounds, mapSize, mapSpacing, } from "../../utils/base-utils";
|
|
4
4
|
import { DEFAULT_TEXT_COLOR } from "../../utils/constants";
|
|
5
5
|
import { getScopedFontFamily, isFontRCFMManaged } from "../../utils/font-utils";
|
|
6
6
|
export const defaultColor = {
|
|
@@ -81,6 +81,7 @@ export function getTextWrapperInlineStyles(colorMode, _restProps, size, backgrou
|
|
|
81
81
|
position: "relative",
|
|
82
82
|
width: mapSize(size.width),
|
|
83
83
|
height: mapSize(size.height),
|
|
84
|
+
...mapFillBounds(size),
|
|
84
85
|
...mapBackground(colorMode, background_color, null),
|
|
85
86
|
});
|
|
86
87
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { TimelineProps } from "../../types/components/timeline";
|
|
2
2
|
import type { ColorMode } from "../../types";
|
|
3
3
|
export declare function getTimelineInlineStyles(props: TimelineProps): {
|
|
4
|
+
padding: string;
|
|
5
|
+
margin: string;
|
|
4
6
|
display: string;
|
|
5
7
|
"grid-template-columns": string;
|
|
6
8
|
"column-gap": string;
|
|
@@ -9,8 +11,6 @@ export declare function getTimelineInlineStyles(props: TimelineProps): {
|
|
|
9
11
|
flex: string;
|
|
10
12
|
width: string;
|
|
11
13
|
height: string;
|
|
12
|
-
padding: string;
|
|
13
|
-
margin: string;
|
|
14
14
|
};
|
|
15
15
|
export declare function getTimelineItemContentInlineStyles(_colorMode: ColorMode, timelineSpacing: {
|
|
16
16
|
text_spacing: number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mapSize, mapSpacing } from "../../utils/base-utils";
|
|
1
|
+
import { mapFillBounds, mapSize, mapSpacing } from "../../utils/base-utils";
|
|
2
2
|
export function getTimelineInlineStyles(props) {
|
|
3
3
|
return {
|
|
4
4
|
display: "grid",
|
|
@@ -9,6 +9,7 @@ export function getTimelineInlineStyles(props) {
|
|
|
9
9
|
flex: "0 1 auto",
|
|
10
10
|
width: mapSize(props.size.width),
|
|
11
11
|
height: mapSize(props.size.height),
|
|
12
|
+
...mapFillBounds(props.size),
|
|
12
13
|
padding: mapSpacing(props.padding),
|
|
13
14
|
margin: mapSpacing(props.margin),
|
|
14
15
|
};
|
|
@@ -1,12 +1,32 @@
|
|
|
1
1
|
<script module lang="ts">
|
|
2
2
|
import Video from "./Video.svelte";
|
|
3
3
|
import { componentDecorator } from "../../stories/component-decorator";
|
|
4
|
+
import { localizationDecorator } from "../../stories/localization-decorator";
|
|
5
|
+
import type { VideoInfo } from "../../types/media";
|
|
4
6
|
import { defineMeta } from "@storybook/addon-svelte-csf";
|
|
5
7
|
|
|
8
|
+
const localizedVideo: VideoInfo = {
|
|
9
|
+
dark: null,
|
|
10
|
+
light: {
|
|
11
|
+
width: 1920,
|
|
12
|
+
height: 1080,
|
|
13
|
+
url: "https://videos.pexels.com/video-files/10288594/10288594-hd_1920_1080_25fps.mp4",
|
|
14
|
+
url_low_res:
|
|
15
|
+
"https://videos.pexels.com/video-files/10288594/10288594-hd_1920_1080_25fps.mp4",
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
6
19
|
const { Story } = defineMeta({
|
|
7
20
|
title: "Components/Video",
|
|
8
21
|
component: Video,
|
|
9
|
-
decorators: [
|
|
22
|
+
decorators: [
|
|
23
|
+
componentDecorator(),
|
|
24
|
+
localizationDecorator({
|
|
25
|
+
defaultLocale: "en_US",
|
|
26
|
+
localizations: {},
|
|
27
|
+
videoLocalizations: { en_US: { localized_video: localizedVideo } },
|
|
28
|
+
}),
|
|
29
|
+
],
|
|
10
30
|
parameters: {
|
|
11
31
|
chromatic: { disableSnapshot: true },
|
|
12
32
|
},
|
|
@@ -139,6 +159,14 @@
|
|
|
139
159
|
}}
|
|
140
160
|
/>
|
|
141
161
|
|
|
162
|
+
<Story
|
|
163
|
+
name="Localized Source"
|
|
164
|
+
args={{
|
|
165
|
+
source: null,
|
|
166
|
+
override_video_lid: "localized_video",
|
|
167
|
+
}}
|
|
168
|
+
/>
|
|
169
|
+
|
|
142
170
|
<Story
|
|
143
171
|
name="Overlay Radial Gradient"
|
|
144
172
|
args={{
|
|
@@ -2,18 +2,22 @@
|
|
|
2
2
|
import { untrack } from "svelte";
|
|
3
3
|
import { getColorModeContext } from "../../stores/color-mode";
|
|
4
4
|
import { useConditionContext } from "../../stores/condition-context.svelte";
|
|
5
|
+
import { getLocalizationContext } from "../../stores/localization";
|
|
5
6
|
import type { VideoProps } from "../../types/components/video";
|
|
6
7
|
import type { VideoFiles, ImageFiles } from "../../types/media";
|
|
7
8
|
import {
|
|
9
|
+
clampFillSize,
|
|
8
10
|
css,
|
|
9
11
|
mapBorderRadius,
|
|
10
12
|
mapColor,
|
|
11
13
|
mapColorMode,
|
|
14
|
+
mapFillBounds,
|
|
12
15
|
mapFitMode,
|
|
13
16
|
mapSize,
|
|
14
17
|
mapSpacing,
|
|
15
18
|
} from "../../utils/base-utils";
|
|
16
19
|
import { resolveOverrideProperties } from "../../utils/style-utils";
|
|
20
|
+
import { resolveLocalizedSource } from "../../utils/source-utils";
|
|
17
21
|
import {
|
|
18
22
|
applyInlineVideoPlaybackFlags,
|
|
19
23
|
subscribeDecorativeVideoAutoplayAttempts,
|
|
@@ -25,9 +29,12 @@
|
|
|
25
29
|
const props: VideoProps = $props();
|
|
26
30
|
|
|
27
31
|
const buildConditionContext = useConditionContext();
|
|
32
|
+
const { getLocalizedVideo } = getLocalizationContext();
|
|
33
|
+
|
|
34
|
+
const conditionContext = $derived(buildConditionContext());
|
|
35
|
+
|
|
28
36
|
const {
|
|
29
37
|
id,
|
|
30
|
-
source,
|
|
31
38
|
fallback_source,
|
|
32
39
|
size,
|
|
33
40
|
mask_shape,
|
|
@@ -45,10 +52,19 @@
|
|
|
45
52
|
} = $derived.by(() => {
|
|
46
53
|
return {
|
|
47
54
|
...props,
|
|
48
|
-
...resolveOverrideProperties(
|
|
55
|
+
...resolveOverrideProperties(conditionContext, props.overrides),
|
|
49
56
|
};
|
|
50
57
|
});
|
|
51
58
|
|
|
59
|
+
const source = $derived(
|
|
60
|
+
resolveLocalizedSource(
|
|
61
|
+
props,
|
|
62
|
+
"override_video_lid",
|
|
63
|
+
conditionContext,
|
|
64
|
+
getLocalizedVideo,
|
|
65
|
+
),
|
|
66
|
+
);
|
|
67
|
+
|
|
52
68
|
const getColorMode = getColorModeContext();
|
|
53
69
|
const colorMode = $derived(getColorMode());
|
|
54
70
|
const video: VideoFiles | null = $derived(
|
|
@@ -145,9 +161,12 @@
|
|
|
145
161
|
const h = videoSize.height;
|
|
146
162
|
if (!Number.isFinite(w) || !Number.isFinite(h) || w <= 0 || h <= 0) {
|
|
147
163
|
// Avoid 0-height box and NaN before metadata (`NaN <= 0` is false); 16:9 placeholder until dimensions resolve.
|
|
148
|
-
|
|
164
|
+
const placeholderHeight =
|
|
165
|
+
wrapperWidth > 0 ? Math.round(wrapperWidth * (9 / 16)) : 200;
|
|
166
|
+
return clampFillSize(placeholderHeight, size.height);
|
|
149
167
|
}
|
|
150
|
-
|
|
168
|
+
const aspectHeight = Math.round(wrapperWidth * (h / w));
|
|
169
|
+
return clampFillSize(aspectHeight, size.height);
|
|
151
170
|
});
|
|
152
171
|
|
|
153
172
|
const style = $derived(
|
|
@@ -161,6 +180,7 @@
|
|
|
161
180
|
...(size.width.type === "fixed"
|
|
162
181
|
? {}
|
|
163
182
|
: { "min-width": 0, "min-height": 0 }),
|
|
183
|
+
...mapFillBounds(size),
|
|
164
184
|
}),
|
|
165
185
|
);
|
|
166
186
|
|
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
import { getColorModeContext } from "../../stores/color-mode";
|
|
5
5
|
import { getPaywallContext } from "../../stores/paywall";
|
|
6
6
|
import type { WalletButtonProps } from "../../types/components/wallet-button";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
css,
|
|
9
|
+
mapFillBounds,
|
|
10
|
+
mapSize,
|
|
11
|
+
mapSpacing,
|
|
12
|
+
px,
|
|
13
|
+
} from "../../utils/base-utils";
|
|
8
14
|
import { DEFAULT_SPACING } from "../../utils/constants";
|
|
9
15
|
import { onDestroy } from "svelte";
|
|
10
16
|
|
|
@@ -62,6 +68,7 @@
|
|
|
62
68
|
position: "relative",
|
|
63
69
|
width: mapSize(size.width),
|
|
64
70
|
height: mapSize(size.height),
|
|
71
|
+
...mapFillBounds(size),
|
|
65
72
|
...mapDimension(dimension),
|
|
66
73
|
gap: px(spacing),
|
|
67
74
|
margin: mapSpacing({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { onMount } from "svelte";
|
|
3
3
|
import type { WebViewProps } from "../../types/components/web-view";
|
|
4
|
-
import { css, mapSize, px } from "../../utils/base-utils";
|
|
4
|
+
import { css, mapFillBounds, mapSize, px } from "../../utils/base-utils";
|
|
5
5
|
import { getStackDimensionContext } from "../stack/stack-dimension-context";
|
|
6
6
|
import { mapFillHeight } from "../stack/stack-utils";
|
|
7
7
|
import { isSameOriginAsHost, sanitizeWebViewUrl } from "./web-view-utils";
|
|
@@ -126,6 +126,7 @@
|
|
|
126
126
|
width.type === "fixed" || widthFit || heightFit ? 0 : 1,
|
|
127
127
|
}),
|
|
128
128
|
"line-height": 0,
|
|
129
|
+
...mapFillBounds(props.size),
|
|
129
130
|
// No transition: the iframe is height:100% of this box, so animating the
|
|
130
131
|
// resize would flash the iframe's own scrollbar mid-change.
|
|
131
132
|
});
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import type { Localizations } from "../types/localization";
|
|
2
|
-
import type { ImageInfo } from "../types/media";
|
|
1
|
+
import type { Localizations, VideoLocalizations } from "../types/localization";
|
|
2
|
+
import type { ImageInfo, VideoInfo } from "../types/media";
|
|
3
3
|
type GetLocalizedString = (id?: string) => string | undefined;
|
|
4
4
|
export type GetLocalizedImage = (id?: string) => ImageInfo | undefined;
|
|
5
|
+
export type GetLocalizedVideo = (id?: string) => VideoInfo | undefined;
|
|
5
6
|
interface LocalizationContext {
|
|
6
7
|
getLocalizedString: GetLocalizedString;
|
|
7
8
|
getLocalizedImage: GetLocalizedImage;
|
|
9
|
+
getLocalizedVideo: GetLocalizedVideo;
|
|
8
10
|
}
|
|
9
11
|
export interface LocalizationContextProps {
|
|
10
12
|
defaultLocale: string;
|
|
11
13
|
selectedLocale?: string;
|
|
12
14
|
localizations: Localizations;
|
|
15
|
+
videoLocalizations?: VideoLocalizations;
|
|
13
16
|
}
|
|
14
17
|
export declare function setLocalizationContext(props: () => LocalizationContextProps): LocalizationContext;
|
|
15
18
|
export declare function getLocalizationContext(): LocalizationContext;
|
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
import { getContext, setContext } from "svelte";
|
|
2
2
|
const key = Symbol("localization");
|
|
3
3
|
export function setLocalizationContext(props) {
|
|
4
|
-
const
|
|
4
|
+
const lookup = (table, id) => {
|
|
5
5
|
if (!id) {
|
|
6
6
|
return undefined;
|
|
7
7
|
}
|
|
8
|
-
const { selectedLocale, defaultLocale
|
|
8
|
+
const { selectedLocale, defaultLocale } = props();
|
|
9
9
|
const locale = selectedLocale ?? defaultLocale;
|
|
10
|
-
const selected =
|
|
11
|
-
const fallback =
|
|
10
|
+
const selected = table[locale]?.[id];
|
|
11
|
+
const fallback = table[defaultLocale]?.[id];
|
|
12
12
|
return selected ?? fallback;
|
|
13
13
|
};
|
|
14
14
|
const getLocalizedString = (id) => {
|
|
15
15
|
if (!id) {
|
|
16
16
|
return "";
|
|
17
17
|
}
|
|
18
|
-
const value =
|
|
18
|
+
const value = lookup(props().localizations, id);
|
|
19
19
|
return typeof value === "string" ? value : undefined;
|
|
20
20
|
};
|
|
21
21
|
const getLocalizedImage = (id) => {
|
|
22
|
-
const value =
|
|
22
|
+
const value = lookup(props().localizations, id);
|
|
23
23
|
return typeof value === "object" ? value : undefined;
|
|
24
24
|
};
|
|
25
|
+
const getLocalizedVideo = (id) => lookup(props().videoLocalizations ?? {}, id);
|
|
25
26
|
const context = {
|
|
26
27
|
getLocalizedString,
|
|
27
28
|
getLocalizedImage,
|
|
29
|
+
getLocalizedVideo,
|
|
28
30
|
};
|
|
29
31
|
setContext(key, context);
|
|
30
32
|
return context;
|
|
@@ -18,7 +18,7 @@ export interface VideoProps extends BaseComponent {
|
|
|
18
18
|
visible?: boolean | null;
|
|
19
19
|
source: VideoInfo | null;
|
|
20
20
|
fallback_source?: ImageInfo | null;
|
|
21
|
-
|
|
21
|
+
override_video_lid?: string | null;
|
|
22
22
|
size: SizeType;
|
|
23
23
|
mask_shape?: RectangleShape | CircleShape | ConcaveShape | ConvexShape | null;
|
|
24
24
|
fit_mode: FitMode;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { ImageInfo } from "./media";
|
|
1
|
+
import type { ImageInfo, VideoInfo } from "./media";
|
|
2
2
|
export type LocalizationValue = string | ImageInfo;
|
|
3
3
|
type Localization = Partial<Record<string, LocalizationValue>>;
|
|
4
4
|
export type Localizations = Partial<Record<string, Localization>>;
|
|
5
|
+
export type VideoLocalizations = Partial<Record<string, Partial<Record<string, VideoInfo>>>>;
|
|
5
6
|
export {};
|
package/dist/types/paywall.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { ColorScheme } from "./colors";
|
|
|
3
3
|
import type { FooterProps } from "./components/footer";
|
|
4
4
|
import type { HeaderProps } from "./components/header";
|
|
5
5
|
import type { StackProps } from "./components/stack";
|
|
6
|
-
import type { Localizations } from "./localization";
|
|
6
|
+
import type { Localizations, VideoLocalizations } from "./localization";
|
|
7
7
|
import type { StateDeclaration } from "./state";
|
|
8
8
|
export interface RootPaywall {
|
|
9
9
|
background?: Background;
|
|
@@ -29,6 +29,7 @@ export interface PaywallData {
|
|
|
29
29
|
default_locale: string;
|
|
30
30
|
components_config: ComponentConfig;
|
|
31
31
|
components_localizations: Localizations;
|
|
32
|
+
components_video_localizations?: VideoLocalizations;
|
|
32
33
|
/** Declared paywall-state keys (key → {type, default}). Absent on paywalls without state. */
|
|
33
34
|
state_declarations?: Record<string, StateDeclaration>;
|
|
34
35
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -57,6 +57,9 @@ type FitSize = {
|
|
|
57
57
|
type FillSize = {
|
|
58
58
|
type: "fill";
|
|
59
59
|
value?: null;
|
|
60
|
+
/** Optional px clamps on the filled axis. */
|
|
61
|
+
min?: number | null;
|
|
62
|
+
max?: number | null;
|
|
60
63
|
};
|
|
61
64
|
export type Size = FixedSize | RelativeSize | FitSize | FillSize;
|
|
62
65
|
export type SizeType = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BorderType, CircleShape, ColorMode, ConcaveShape, ConvexShape, PillShape, RectangleShape, ShadowType, Size, Spacing } from "../types";
|
|
1
|
+
import type { BorderType, CircleShape, ColorMode, ConcaveShape, ConvexShape, PillShape, RectangleShape, ShadowType, Size, SizeType, Spacing } from "../types";
|
|
2
2
|
import type { ColorGradientInfo, ColorGradientScheme, ColorScheme } from "../types/colors";
|
|
3
3
|
import type { FitMode } from "../types/media";
|
|
4
4
|
export type CSS = Record<string, string | number>;
|
|
@@ -9,6 +9,10 @@ export declare function mapColorMode<T>(colorMode: ColorMode, value: {
|
|
|
9
9
|
dark?: T | null;
|
|
10
10
|
}): T;
|
|
11
11
|
export declare function mapSize(size: Size): string;
|
|
12
|
+
/** Spread after flex min-size resets so authored fill bounds win. */
|
|
13
|
+
export declare function mapFillBounds(size: Partial<SizeType>): CSS;
|
|
14
|
+
/** Apply fill bounds to a layout-derived size before the browser measures it. */
|
|
15
|
+
export declare function clampFillSize(value: number, size: Size): number;
|
|
12
16
|
export declare function mapSpacing(spacing?: Spacing | null): string;
|
|
13
17
|
export declare function mapColorInfo(info: ColorGradientInfo): string;
|
|
14
18
|
export declare function mapColor(colorMode: ColorMode, scheme: ColorScheme | ColorGradientScheme | null | undefined): string;
|
package/dist/utils/base-utils.js
CHANGED
|
@@ -33,6 +33,30 @@ export function mapSize(size) {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
+
function mapAxisFillBounds(size, axis) {
|
|
37
|
+
if (size.type !== "fill") {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
...(size.min != null ? { [`min-${axis}`]: px(size.min) } : {}),
|
|
42
|
+
...(size.max != null ? { [`max-${axis}`]: px(size.max) } : {}),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Spread after flex min-size resets so authored fill bounds win. */
|
|
46
|
+
export function mapFillBounds(size) {
|
|
47
|
+
return {
|
|
48
|
+
...(size.width ? mapAxisFillBounds(size.width, "width") : {}),
|
|
49
|
+
...(size.height ? mapAxisFillBounds(size.height, "height") : {}),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** Apply fill bounds to a layout-derived size before the browser measures it. */
|
|
53
|
+
export function clampFillSize(value, size) {
|
|
54
|
+
if (size.type !== "fill") {
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
const maxClamped = size.max == null ? value : Math.min(value, size.max);
|
|
58
|
+
return size.min == null ? maxClamped : Math.max(maxClamped, size.min);
|
|
59
|
+
}
|
|
36
60
|
export function mapSpacing(spacing) {
|
|
37
61
|
const { top = 0, bottom = 0, leading = 0, trailing = 0, } = spacing ?? DEFAULT_SPACING;
|
|
38
62
|
return mapTuple(top, trailing, bottom, leading);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Overrides } from "../types/overrides";
|
|
2
|
+
import { type ConditionContext } from "./style-utils";
|
|
3
|
+
type LocalizedSourceFields<Source, LidKey extends string> = {
|
|
4
|
+
source?: Source;
|
|
5
|
+
} & Partial<Record<LidKey, string | null>>;
|
|
6
|
+
type LocalizedSourceProps<Source, LidKey extends string> = LocalizedSourceFields<Source, LidKey> & {
|
|
7
|
+
source: Source;
|
|
8
|
+
overrides?: Overrides<LocalizedSourceFields<Source, LidKey>>;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Resolves the media source to render for a component, preferring a matching
|
|
12
|
+
* override but defaulting to the component's source. At each level, a
|
|
13
|
+
* localized source referenced by `lidKey` wins over the inline `source`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveLocalizedSource<Source, LidKey extends string>(props: LocalizedSourceProps<Source, LidKey>, lidKey: LidKey, context: ConditionContext, getLocalizedSource: (id?: string) => NonNullable<Source> | undefined): Source;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { filterMatchingOverrides, } from "./style-utils";
|
|
2
|
+
/**
|
|
3
|
+
* Resolves the media source to render for a component, preferring a matching
|
|
4
|
+
* override but defaulting to the component's source. At each level, a
|
|
5
|
+
* localized source referenced by `lidKey` wins over the inline `source`.
|
|
6
|
+
*/
|
|
7
|
+
export function resolveLocalizedSource(props, lidKey, context, getLocalizedSource) {
|
|
8
|
+
const resolve = (source, lid) => (lid ? (getLocalizedSource(lid) ?? source) : source);
|
|
9
|
+
let resolvedOverrideSource;
|
|
10
|
+
for (const override of filterMatchingOverrides(context, props.overrides)) {
|
|
11
|
+
const source = resolve(override.properties.source, override.properties[lidKey]);
|
|
12
|
+
if (source) {
|
|
13
|
+
resolvedOverrideSource = source;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return (resolvedOverrideSource ??
|
|
17
|
+
resolve(props.source, props[lidKey]) ??
|
|
18
|
+
props.source);
|
|
19
|
+
}
|
|
@@ -35,7 +35,7 @@ export type ConditionContext = {
|
|
|
35
35
|
* Returns the overrides (in array order) whose conditions all match (AND) the
|
|
36
36
|
* given context, without merging their properties.
|
|
37
37
|
*/
|
|
38
|
-
export declare const filterMatchingOverrides: <T
|
|
38
|
+
export declare const filterMatchingOverrides: <T>(context: ConditionContext, overrides?: Overrides<T>) => Overrides<T>;
|
|
39
39
|
/**
|
|
40
40
|
* Merges the properties of every override whose conditions all match (AND),
|
|
41
41
|
* applying them in override-array order (later overrides overwrite earlier ones).
|
package/package.json
CHANGED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { ImageProps } from "../../types/components/image";
|
|
2
|
-
import type { ImageInfo } from "../../types/media";
|
|
3
|
-
import { type ConditionContext } from "../../utils/style-utils";
|
|
4
|
-
import type { GetLocalizedImage } from "../../stores/localization";
|
|
5
|
-
/**
|
|
6
|
-
* Resolves the ImageInfo to render for an image component, preferring
|
|
7
|
-
* a matching override but defaulting to the image source.
|
|
8
|
-
*/
|
|
9
|
-
export declare function resolveImageSource(props: ImageProps, context: ConditionContext, getLocalizedImage: GetLocalizedImage): ImageInfo;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { filterMatchingOverrides, } from "../../utils/style-utils";
|
|
2
|
-
/**
|
|
3
|
-
* Resolves the ImageInfo to render for an image component, preferring
|
|
4
|
-
* a matching override but defaulting to the image source.
|
|
5
|
-
*/
|
|
6
|
-
export function resolveImageSource(props, context, getLocalizedImage) {
|
|
7
|
-
const resolveLocalizedSource = (source, overrideSourceLid) => overrideSourceLid
|
|
8
|
-
? (getLocalizedImage(overrideSourceLid) ?? source)
|
|
9
|
-
: source;
|
|
10
|
-
let resolvedOverrideImage;
|
|
11
|
-
for (const override of filterMatchingOverrides(context, props.overrides)) {
|
|
12
|
-
const image = resolveLocalizedSource(override.properties.source, override.properties.override_source_lid);
|
|
13
|
-
if (image) {
|
|
14
|
-
resolvedOverrideImage = image;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return (resolvedOverrideImage ??
|
|
18
|
-
resolveLocalizedSource(props.source, props.override_source_lid) ??
|
|
19
|
-
props.source);
|
|
20
|
-
}
|