Merge remote-tracking branch 'origin/main' into feat/list-view-sticky-headers

This commit is contained in:
Nathan Walker
2025-10-30 19:21:42 -07:00
10 changed files with 133 additions and 29 deletions

View File

@@ -97,11 +97,6 @@ interface ApplicationEvents {
*/
on(event: 'livesync', callback: (args: ApplicationEventData) => void, thisArg?: any): void;
/**
* This event is raised when application css is changed.
*/
on(event: 'cssChanged', callback: (args: CssChangedEventData) => void, thisArg?: any): void;
/**
* This event is raised on application launchEvent.
*/

View File

@@ -19,7 +19,7 @@ export class ImageAssetBase extends Observable implements ImageAssetDefinition {
}
set options(value: ImageAssetOptions) {
this._options = value;
this._options = normalizeImageAssetOptions(value);
}
get nativeImage(): any {
@@ -35,6 +35,35 @@ export class ImageAssetBase extends Observable implements ImageAssetDefinition {
}
}
function toPositiveInt(value: any): number {
if (value == null) {
return 0;
}
if (typeof value === 'number') {
return value > 0 ? Math.floor(value) : 0;
}
if (typeof value === 'string') {
const parsed = parseInt(value, 10);
return isNaN(parsed) || parsed <= 0 ? 0 : parsed;
}
return 0;
}
function normalizeImageAssetOptions(options: ImageAssetOptions): ImageAssetOptions {
const normalized = options ? { ...options } : ({} as ImageAssetOptions);
// Coerce potential string values to positive integers; fallback to 0
// to trigger default sizing downstream
(normalized as any).width = toPositiveInt((options as any)?.width);
(normalized as any).height = toPositiveInt((options as any)?.height);
if (typeof normalized.keepAspectRatio !== 'boolean') {
normalized.keepAspectRatio = true;
}
if (typeof normalized.autoScaleFactor !== 'boolean') {
normalized.autoScaleFactor = true;
}
return normalized;
}
export function getAspectSafeDimensions(sourceWidth, sourceHeight, reqWidth, reqHeight) {
const widthCoef = sourceWidth / reqWidth;
const heightCoef = sourceHeight / reqHeight;
@@ -47,8 +76,9 @@ export function getAspectSafeDimensions(sourceWidth, sourceHeight, reqWidth, req
}
export function getRequestedImageSize(src: { width: number; height: number }, options: ImageAssetOptions): { width: number; height: number } {
let reqWidth = options.width || Math.min(src.width, Screen.mainScreen.widthPixels);
let reqHeight = options.height || Math.min(src.height, Screen.mainScreen.heightPixels);
const normalized = normalizeImageAssetOptions(options);
let reqWidth = normalized.width || Math.min(src.width, Screen.mainScreen.widthPixels);
let reqHeight = normalized.height || Math.min(src.height, Screen.mainScreen.heightPixels);
if (options && options.keepAspectRatio) {
const safeAspectSize = getAspectSafeDimensions(src.width, src.height, reqWidth, reqHeight);

View File

@@ -0,0 +1,27 @@
import { getRequestedImageSize } from './image-asset-common';
describe('ImageAssetOptions normalization', () => {
it('coerces string width/height to numbers', () => {
const src = { width: 2000, height: 1500 };
const result = getRequestedImageSize(src as any, { width: '300' as any, height: '200' as any, keepAspectRatio: false, autoScaleFactor: true } as any);
expect(result.width).toBe(300);
expect(result.height).toBe(200);
});
it('falls back to defaults when invalid strings provided', () => {
const src = { width: 800, height: 600 };
const result = getRequestedImageSize(src as any, { width: 'abc' as any, height: '' as any, keepAspectRatio: false } as any);
// should fall back to screen pixel defaults via getRequestedImageSize, but since
// we cannot easily control Screen.mainScreen here, we at least assert they are > 0
expect(result.width).toBeGreaterThan(0);
expect(result.height).toBeGreaterThan(0);
});
it('respects keepAspectRatio by adjusting to safe dimensions', () => {
const src = { width: 2000, height: 1000 };
const result = getRequestedImageSize(src as any, { width: '500' as any, height: '500' as any, keepAspectRatio: true } as any);
// current implementation scales using the smaller coefficient (min), so expect 1000x500
expect(result.width).toBe(1000);
expect(result.height).toBe(500);
});
});

View File

@@ -181,13 +181,14 @@ export class ImageSource implements ImageSourceDefinition {
const textBounds = new android.graphics.Rect();
paint.getTextBounds(source, 0, source.length, textBounds);
const textWidth = textBounds.width();
const textHeight = textBounds.height();
const padding = 1;
const textWidth = textBounds.width() + padding * 2;
const textHeight = textBounds.height() + padding * 2;
if (textWidth > 0 && textHeight > 0) {
const bitmap = android.graphics.Bitmap.createBitmap(textWidth, textHeight, android.graphics.Bitmap.Config.ARGB_8888);
const canvas = new android.graphics.Canvas(bitmap);
canvas.drawText(source, -textBounds.left, -textBounds.top, paint);
canvas.drawText(source, -textBounds.left + padding, -textBounds.top + padding, paint);
return new ImageSource(bitmap);
}

View File

Binary file not shown.