@use-voltra/android 2.0.0-rc.4 → 2.1.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/build/commonjs/index.js +20 -0
- package/build/commonjs/index.js.map +1 -1
- package/build/module/index.js +6 -0
- package/build/module/index.js.map +1 -1
- package/build/typescript/commonjs/index.d.ts +3 -0
- package/build/typescript/commonjs/index.d.ts.map +1 -1
- package/build/typescript/commonjs/styles/types.d.ts +2 -0
- package/build/typescript/commonjs/styles/types.d.ts.map +1 -1
- package/build/typescript/module/index.d.ts +3 -0
- package/build/typescript/module/index.d.ts.map +1 -1
- package/build/typescript/module/styles/types.d.ts +2 -0
- package/build/typescript/module/styles/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/dynamic-colors.ts +33 -0
- package/src/index.ts +99 -0
- package/src/internal.ts +2 -0
- package/src/jsx/AreaMark.tsx +16 -0
- package/src/jsx/BarMark.tsx +18 -0
- package/src/jsx/Box.tsx +5 -0
- package/src/jsx/Button.tsx +5 -0
- package/src/jsx/Chart.tsx +123 -0
- package/src/jsx/CheckBox.tsx +5 -0
- package/src/jsx/CircleIconButton.tsx +14 -0
- package/src/jsx/CircularProgressIndicator.tsx +7 -0
- package/src/jsx/Column.tsx +5 -0
- package/src/jsx/FilledButton.tsx +14 -0
- package/src/jsx/Image.tsx +21 -0
- package/src/jsx/LazyColumn.tsx +5 -0
- package/src/jsx/LazyVerticalGrid.tsx +45 -0
- package/src/jsx/LineMark.tsx +17 -0
- package/src/jsx/LinearProgressIndicator.tsx +7 -0
- package/src/jsx/OutlineButton.tsx +14 -0
- package/src/jsx/PointMark.tsx +16 -0
- package/src/jsx/RadioButton.tsx +5 -0
- package/src/jsx/Row.tsx +5 -0
- package/src/jsx/RuleMark.tsx +15 -0
- package/src/jsx/Scaffold.tsx +5 -0
- package/src/jsx/SectorMark.tsx +17 -0
- package/src/jsx/Spacer.tsx +5 -0
- package/src/jsx/SquareIconButton.tsx +14 -0
- package/src/jsx/Switch.tsx +5 -0
- package/src/jsx/Text.tsx +10 -0
- package/src/jsx/TitleBar.tsx +18 -0
- package/src/jsx/baseProps.tsx +13 -0
- package/src/jsx/chart-types.ts +2 -0
- package/src/jsx/createVoltraComponent.ts +2 -0
- package/src/jsx/index.ts +1 -0
- package/src/jsx/primitives.ts +28 -0
- package/src/jsx/props/.generated +15 -0
- package/src/jsx/props/AndroidBox.ts +19 -0
- package/src/jsx/props/AndroidButton.ts +10 -0
- package/src/jsx/props/AndroidChart.ts +20 -0
- package/src/jsx/props/AndroidCheckBox.ts +20 -0
- package/src/jsx/props/AndroidCircleIconButton.ts +18 -0
- package/src/jsx/props/AndroidCircularProgressIndicator.ts +10 -0
- package/src/jsx/props/AndroidColumn.ts +12 -0
- package/src/jsx/props/AndroidFilledButton.ts +20 -0
- package/src/jsx/props/AndroidImage.ts +26 -0
- package/src/jsx/props/AndroidLazyColumn.ts +10 -0
- package/src/jsx/props/AndroidLazyVerticalGrid.ts +12 -0
- package/src/jsx/props/AndroidLinearProgressIndicator.ts +14 -0
- package/src/jsx/props/AndroidOutlineButton.ts +18 -0
- package/src/jsx/props/AndroidRadioButton.ts +22 -0
- package/src/jsx/props/AndroidRow.ts +12 -0
- package/src/jsx/props/AndroidScaffold.ts +12 -0
- package/src/jsx/props/AndroidSpacer.ts +7 -0
- package/src/jsx/props/AndroidSquareIconButton.ts +18 -0
- package/src/jsx/props/AndroidSwitch.ts +24 -0
- package/src/jsx/props/AndroidText.ts +14 -0
- package/src/jsx/props/AndroidTitleBar.ts +18 -0
- package/src/live-update/renderer.ts +36 -0
- package/src/live-update/types.ts +44 -0
- package/src/ongoing-notification/components.tsx +49 -0
- package/src/ongoing-notification/renderer.ts +360 -0
- package/src/ongoing-notification/types.ts +199 -0
- package/src/payload/component-ids.ts +89 -0
- package/src/preload-utils.ts +78 -0
- package/src/renderer/index.ts +6 -0
- package/src/renderer/renderer.ts +25 -0
- package/src/server.ts +19 -0
- package/src/styles/types.ts +114 -0
- package/src/types.ts +38 -0
- package/src/utils/index.ts +0 -0
- package/src/widgets/renderer.ts +90 -0
- package/src/widgets/types.ts +62 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// 🤖 AUTO-GENERATED from data/components.json
|
|
3
|
+
// DO NOT EDIT MANUALLY - Changes will be overwritten
|
|
4
|
+
// Schema version: 1.0.0
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Mapping from Android component name to numeric ID
|
|
8
|
+
* Component IDs are assigned sequentially based on order in components.json (0-indexed)
|
|
9
|
+
*/
|
|
10
|
+
export const ANDROID_COMPONENT_NAME_TO_ID: Record<string, number> = {
|
|
11
|
+
AndroidFilledButton: 0,
|
|
12
|
+
AndroidImage: 1,
|
|
13
|
+
AndroidSwitch: 2,
|
|
14
|
+
AndroidCheckBox: 3,
|
|
15
|
+
AndroidRadioButton: 4,
|
|
16
|
+
AndroidBox: 5,
|
|
17
|
+
AndroidButton: 6,
|
|
18
|
+
AndroidCircleIconButton: 7,
|
|
19
|
+
AndroidCircularProgressIndicator: 8,
|
|
20
|
+
AndroidColumn: 9,
|
|
21
|
+
AndroidLazyColumn: 10,
|
|
22
|
+
AndroidLazyVerticalGrid: 11,
|
|
23
|
+
AndroidLinearProgressIndicator: 12,
|
|
24
|
+
AndroidOutlineButton: 13,
|
|
25
|
+
AndroidRow: 14,
|
|
26
|
+
AndroidScaffold: 15,
|
|
27
|
+
AndroidSpacer: 16,
|
|
28
|
+
AndroidSquareIconButton: 17,
|
|
29
|
+
AndroidText: 18,
|
|
30
|
+
AndroidTitleBar: 19,
|
|
31
|
+
AndroidChart: 20,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Mapping from numeric ID to Android component name
|
|
36
|
+
*/
|
|
37
|
+
export const ANDROID_COMPONENT_ID_TO_NAME: Record<number, string> = {
|
|
38
|
+
0: 'AndroidFilledButton',
|
|
39
|
+
1: 'AndroidImage',
|
|
40
|
+
2: 'AndroidSwitch',
|
|
41
|
+
3: 'AndroidCheckBox',
|
|
42
|
+
4: 'AndroidRadioButton',
|
|
43
|
+
5: 'AndroidBox',
|
|
44
|
+
6: 'AndroidButton',
|
|
45
|
+
7: 'AndroidCircleIconButton',
|
|
46
|
+
8: 'AndroidCircularProgressIndicator',
|
|
47
|
+
9: 'AndroidColumn',
|
|
48
|
+
10: 'AndroidLazyColumn',
|
|
49
|
+
11: 'AndroidLazyVerticalGrid',
|
|
50
|
+
12: 'AndroidLinearProgressIndicator',
|
|
51
|
+
13: 'AndroidOutlineButton',
|
|
52
|
+
14: 'AndroidRow',
|
|
53
|
+
15: 'AndroidScaffold',
|
|
54
|
+
16: 'AndroidSpacer',
|
|
55
|
+
17: 'AndroidSquareIconButton',
|
|
56
|
+
18: 'AndroidText',
|
|
57
|
+
19: 'AndroidTitleBar',
|
|
58
|
+
20: 'AndroidChart',
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get Android component ID from name
|
|
63
|
+
* @throws Error if component name is not found
|
|
64
|
+
*/
|
|
65
|
+
export function getAndroidComponentId(name: string): number {
|
|
66
|
+
const id = ANDROID_COMPONENT_NAME_TO_ID[name]
|
|
67
|
+
if (id === undefined) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`Unknown Android component name: "${name}". Available components: ${Object.keys(
|
|
70
|
+
ANDROID_COMPONENT_NAME_TO_ID
|
|
71
|
+
).join(', ')}`
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
return id
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get Android component name from ID
|
|
79
|
+
* @throws Error if component ID is not found
|
|
80
|
+
*/
|
|
81
|
+
export function getAndroidComponentName(id: number): string {
|
|
82
|
+
const name = ANDROID_COMPONENT_ID_TO_NAME[id]
|
|
83
|
+
if (name === undefined) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`Unknown Android component ID: ${id}. Valid IDs: 0-${Object.keys(ANDROID_COMPONENT_ID_TO_NAME).length - 1}`
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
return name
|
|
89
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PreloadImageFailure,
|
|
3
|
+
PreloadImageOptions,
|
|
4
|
+
PreloadImageSvgOptions,
|
|
5
|
+
PreloadImageUrlOptions,
|
|
6
|
+
PreloadImagesResult,
|
|
7
|
+
} from './types.js'
|
|
8
|
+
|
|
9
|
+
type NativePreloadModule = {
|
|
10
|
+
preloadUrlImages(images: PreloadImageUrlOptions[]): Promise<PreloadImagesResult>
|
|
11
|
+
preloadSvgImages(images: PreloadImageSvgOptions[]): Promise<PreloadImagesResult>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const emptyResult: PreloadImagesResult = {
|
|
15
|
+
succeeded: [],
|
|
16
|
+
failed: [],
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function hasStringValue(value: unknown): value is string {
|
|
20
|
+
return typeof value === 'string' && value.trim().length > 0
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function errorMessage(error: unknown): string {
|
|
24
|
+
return error instanceof Error ? error.message : 'Unknown error'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function preloadGroup<T extends PreloadImageOptions>(
|
|
28
|
+
images: T[],
|
|
29
|
+
preload: (images: T[]) => Promise<PreloadImagesResult>
|
|
30
|
+
): Promise<PreloadImagesResult> {
|
|
31
|
+
if (images.length === 0) {
|
|
32
|
+
return emptyResult
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
return await preload(images)
|
|
37
|
+
} catch (error) {
|
|
38
|
+
return {
|
|
39
|
+
succeeded: [],
|
|
40
|
+
failed: images.map((image) => ({
|
|
41
|
+
key: image.key,
|
|
42
|
+
error: errorMessage(error),
|
|
43
|
+
})),
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function dispatchPreloadImages(
|
|
49
|
+
nativeModule: NativePreloadModule,
|
|
50
|
+
images: PreloadImageOptions[]
|
|
51
|
+
): Promise<PreloadImagesResult> {
|
|
52
|
+
const urlImages: PreloadImageUrlOptions[] = []
|
|
53
|
+
const svgImages: PreloadImageSvgOptions[] = []
|
|
54
|
+
const invalidImages: PreloadImageFailure[] = []
|
|
55
|
+
|
|
56
|
+
for (const image of images) {
|
|
57
|
+
if ('svg' in image && hasStringValue(image.svg)) {
|
|
58
|
+
svgImages.push(image)
|
|
59
|
+
} else if ('url' in image && hasStringValue(image.url)) {
|
|
60
|
+
urlImages.push(image)
|
|
61
|
+
} else {
|
|
62
|
+
invalidImages.push({
|
|
63
|
+
key: image.key,
|
|
64
|
+
error: `Image '${image.key}' must provide either url or svg`,
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const [urlResult, svgResult] = await Promise.all([
|
|
70
|
+
preloadGroup(urlImages, (group) => nativeModule.preloadUrlImages(group)),
|
|
71
|
+
preloadGroup(svgImages, (group) => nativeModule.preloadSvgImages(group)),
|
|
72
|
+
])
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
succeeded: [...urlResult.succeeded, ...svgResult.succeeded],
|
|
76
|
+
failed: [...invalidImages, ...urlResult.failed, ...svgResult.failed],
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ComponentRegistry,
|
|
3
|
+
createVoltraRenderer as createCoreVoltraRenderer,
|
|
4
|
+
renderVariantToJson,
|
|
5
|
+
VOLTRA_PAYLOAD_VERSION,
|
|
6
|
+
} from '@use-voltra/core'
|
|
7
|
+
import type { ReactNode } from 'react'
|
|
8
|
+
|
|
9
|
+
import { getAndroidComponentId } from '../payload/component-ids.js'
|
|
10
|
+
import type { VoltraNodeJson } from '../types.js'
|
|
11
|
+
|
|
12
|
+
export const androidComponentRegistry: ComponentRegistry = {
|
|
13
|
+
getComponentId: (name: string) => getAndroidComponentId(name),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { VOLTRA_PAYLOAD_VERSION }
|
|
17
|
+
export type { ComponentRegistry }
|
|
18
|
+
|
|
19
|
+
export const renderAndroidVariantToJson = (element: ReactNode): VoltraNodeJson => {
|
|
20
|
+
return renderVariantToJson(element, androidComponentRegistry)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const createVoltraRenderer = (componentRegistry: ComponentRegistry = androidComponentRegistry) => {
|
|
24
|
+
return createCoreVoltraRenderer(componentRegistry)
|
|
25
|
+
}
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { AndroidOngoingNotification } from './ongoing-notification/components.js'
|
|
2
|
+
export {
|
|
3
|
+
renderAndroidOngoingNotificationPayload,
|
|
4
|
+
renderAndroidOngoingNotificationPayloadToJson,
|
|
5
|
+
} from './ongoing-notification/renderer.js'
|
|
6
|
+
export type {
|
|
7
|
+
AndroidOngoingNotificationActionPayload,
|
|
8
|
+
AndroidOngoingNotificationActionProps,
|
|
9
|
+
AndroidOngoingNotificationBigTextPayload,
|
|
10
|
+
AndroidOngoingNotificationBigTextProps,
|
|
11
|
+
AndroidOngoingNotificationContent,
|
|
12
|
+
AndroidOngoingNotificationPayload,
|
|
13
|
+
AndroidOngoingNotificationProgressPayload,
|
|
14
|
+
AndroidOngoingNotificationProgressPoint,
|
|
15
|
+
AndroidOngoingNotificationProgressProps,
|
|
16
|
+
AndroidOngoingNotificationProgressSegment,
|
|
17
|
+
} from './ongoing-notification/types.js'
|
|
18
|
+
export { renderAndroidWidgetToString } from './widgets/renderer.js'
|
|
19
|
+
export type { AndroidColorValue, AndroidDynamicColorRole, AndroidDynamicColorToken } from './dynamic-colors.js'
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Android-specific style types for Voltra components.
|
|
3
|
+
* These types mirror the supported properties in the Android implementation (Glance).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { AndroidColorValue } from '../dynamic-colors.js'
|
|
7
|
+
|
|
8
|
+
export type StyleProp<T> = T | T[] | null | undefined | false
|
|
9
|
+
|
|
10
|
+
export type VoltraAndroidViewStyle = {
|
|
11
|
+
/** Width of the component. Supports fixed numbers (dp) or "100%" to fill. */
|
|
12
|
+
width?: number | string
|
|
13
|
+
/** Height of the component. Supports fixed numbers (dp) or "100%" to fill. */
|
|
14
|
+
height?: number | string
|
|
15
|
+
/** Minimum width (Note: Limited support in Glance) */
|
|
16
|
+
minWidth?: number
|
|
17
|
+
/** Maximum width (Note: Limited support in Glance) */
|
|
18
|
+
maxWidth?: number
|
|
19
|
+
/** Minimum height (Note: Limited support in Glance) */
|
|
20
|
+
minHeight?: number
|
|
21
|
+
/** Maximum height (Note: Limited support in Glance) */
|
|
22
|
+
maxHeight?: number
|
|
23
|
+
|
|
24
|
+
/** Flex weight for the component (maps to .defaultWeight() in Glance) */
|
|
25
|
+
flex?: number
|
|
26
|
+
/** Flex grow weight for the component (maps to .defaultWeight() in Glance) */
|
|
27
|
+
flexGrow?: number
|
|
28
|
+
|
|
29
|
+
/** Aspect ratio of the component (Note: Not supported in Glance) */
|
|
30
|
+
aspectRatio?: number
|
|
31
|
+
|
|
32
|
+
/** Inner padding for the component */
|
|
33
|
+
padding?: number
|
|
34
|
+
paddingTop?: number
|
|
35
|
+
paddingBottom?: number
|
|
36
|
+
paddingLeft?: number
|
|
37
|
+
paddingRight?: number
|
|
38
|
+
paddingVertical?: number
|
|
39
|
+
paddingHorizontal?: number
|
|
40
|
+
|
|
41
|
+
/** Gap between children (Note: Supported by specific layouts like LazyColumn/Row) */
|
|
42
|
+
gap?: number
|
|
43
|
+
|
|
44
|
+
/** Alignment of children along the cross axis */
|
|
45
|
+
alignItems?: 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline'
|
|
46
|
+
/** Alignment of children along the main axis */
|
|
47
|
+
justifyContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly'
|
|
48
|
+
|
|
49
|
+
/** Absolute positioning from the left (maps to Offset in Glance) */
|
|
50
|
+
left?: number
|
|
51
|
+
/** Absolute positioning from the top (maps to Offset in Glance) */
|
|
52
|
+
top?: number
|
|
53
|
+
/** Z-index for layering (Note: Limited support in Glance) */
|
|
54
|
+
zIndex?: number
|
|
55
|
+
|
|
56
|
+
/** Background color of the component */
|
|
57
|
+
backgroundColor?: AndroidColorValue
|
|
58
|
+
/** CSS gradient background image, e.g. linear-gradient(to right, red, blue) */
|
|
59
|
+
backgroundImage?: string
|
|
60
|
+
/** Corner radius for the component (requires Android 12+) */
|
|
61
|
+
borderRadius?: number
|
|
62
|
+
/** Border width (Note: Not yet implemented in Glance) */
|
|
63
|
+
borderWidth?: number
|
|
64
|
+
/** Border color (Note: Not yet implemented in Glance) */
|
|
65
|
+
borderColor?: AndroidColorValue
|
|
66
|
+
|
|
67
|
+
/** Opacity of the component (Note: Not supported in Glance - apply alpha to colors instead) */
|
|
68
|
+
opacity?: number
|
|
69
|
+
/** Controls if content is clipped to the border (maps to clipToOutline) */
|
|
70
|
+
overflow?: 'visible' | 'hidden'
|
|
71
|
+
|
|
72
|
+
/** Controls layout display mode */
|
|
73
|
+
display?: 'none' | 'flex'
|
|
74
|
+
/** Controls component visibility */
|
|
75
|
+
visibility?: 'visible' | 'hidden' | 'invisible'
|
|
76
|
+
|
|
77
|
+
/** Transform effects (Note: Not supported in Glance) */
|
|
78
|
+
transform?: ({ rotate: string } | { rotateZ: string } | { scale: number } | { scaleX: number } | { scaleY: number })[]
|
|
79
|
+
|
|
80
|
+
/** Shadow properties (Note: Not supported in Glance) */
|
|
81
|
+
shadowColor?: AndroidColorValue
|
|
82
|
+
shadowOffset?: { width: number; height: number }
|
|
83
|
+
shadowOpacity?: number
|
|
84
|
+
shadowRadius?: number
|
|
85
|
+
|
|
86
|
+
/** Glass effect (iOS-specific, included for API compatibility) */
|
|
87
|
+
glassEffect?: 'clear' | 'identity' | 'regular' | 'none'
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type VoltraAndroidTextStyle = VoltraAndroidViewStyle & {
|
|
91
|
+
/** Text color */
|
|
92
|
+
color?: AndroidColorValue
|
|
93
|
+
/** Font size in sp */
|
|
94
|
+
fontSize?: number
|
|
95
|
+
/** Font weight */
|
|
96
|
+
fontWeight?: 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900' | number
|
|
97
|
+
/** Font family name. When used with `renderAsBitmap`, loads custom .ttf/.otf from `assets/fonts/` */
|
|
98
|
+
fontFamily?: string
|
|
99
|
+
/** Text alignment */
|
|
100
|
+
textAlign?: 'auto' | 'left' | 'right' | 'center' | 'justify' | 'start' | 'end'
|
|
101
|
+
/** Text decoration line */
|
|
102
|
+
textDecorationLine?: 'none' | 'underline' | 'line-through' | 'underline line-through'
|
|
103
|
+
/** Line height (maps to line spacing in Glance) */
|
|
104
|
+
lineHeight?: number
|
|
105
|
+
/** Letter spacing (Note: Not supported in Glance) */
|
|
106
|
+
letterSpacing?: number
|
|
107
|
+
/** Font variants (Note: Not supported in Glance) */
|
|
108
|
+
fontVariant?: ('small-caps' | 'tabular-nums')[]
|
|
109
|
+
/** Maximum number of lines to display (maps to lineLimit) */
|
|
110
|
+
numberOfLines?: number
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export type VoltraAndroidStyleProp = StyleProp<VoltraAndroidViewStyle>
|
|
114
|
+
export type VoltraAndroidTextStyleProp = StyleProp<VoltraAndroidTextStyle>
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type { VoltraElementJson, VoltraElementRef, VoltraNodeJson, VoltraPropValue } from '@use-voltra/core'
|
|
2
|
+
|
|
3
|
+
export type EventSubscription = {
|
|
4
|
+
remove: () => void
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
type PreloadImageBaseOptions = {
|
|
8
|
+
key: string
|
|
9
|
+
width?: number
|
|
10
|
+
height?: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type PreloadImageUrlOptions = PreloadImageBaseOptions & {
|
|
14
|
+
url: string
|
|
15
|
+
method?: 'GET' | 'POST' | 'PUT'
|
|
16
|
+
headers?: Record<string, string>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type PreloadImageSvgOptions = PreloadImageBaseOptions & {
|
|
20
|
+
svg: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type PreloadImageOptions = PreloadImageUrlOptions | PreloadImageSvgOptions
|
|
24
|
+
|
|
25
|
+
export type PreloadImageFailure = {
|
|
26
|
+
key: string
|
|
27
|
+
error: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type PreloadImagesResult = {
|
|
31
|
+
succeeded: string[]
|
|
32
|
+
failed: PreloadImageFailure[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type WidgetServerCredentials = {
|
|
36
|
+
token: string
|
|
37
|
+
headers?: Record<string, string>
|
|
38
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createElement, Fragment as ReactFragment, type ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import { getAndroidComponentId } from '../payload/component-ids.js'
|
|
4
|
+
import { ComponentRegistry, createVoltraRenderer } from '../renderer/renderer.js'
|
|
5
|
+
import type { AndroidWidgetVariants } from './types.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Android component registry that uses Android component ID mappings
|
|
9
|
+
*/
|
|
10
|
+
const androidComponentRegistry: ComponentRegistry = {
|
|
11
|
+
getComponentId: (name: string) => getAndroidComponentId(name),
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type AndroidWidgetRenderOptions = Record<string, never>
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Renders Android widget variants to JSON with size breakpoints.
|
|
18
|
+
*
|
|
19
|
+
* Output format:
|
|
20
|
+
* {
|
|
21
|
+
* "v": 1,
|
|
22
|
+
* "variants": {
|
|
23
|
+
* "150x100": { ... node tree ... },
|
|
24
|
+
* "150x200": { ... node tree ... },
|
|
25
|
+
* "215x100": { ... node tree ... }
|
|
26
|
+
* },
|
|
27
|
+
* "s": [...shared styles...],
|
|
28
|
+
* "e": [...shared elements...]
|
|
29
|
+
* }
|
|
30
|
+
*/
|
|
31
|
+
export const renderAndroidWidgetToJson = (
|
|
32
|
+
variants: AndroidWidgetVariants,
|
|
33
|
+
_options?: AndroidWidgetRenderOptions
|
|
34
|
+
): Record<string, any> => {
|
|
35
|
+
const renderer = createVoltraRenderer(androidComponentRegistry)
|
|
36
|
+
|
|
37
|
+
// Add each size variant with key format "WIDTHxHEIGHT"
|
|
38
|
+
for (const { size, content } of variants) {
|
|
39
|
+
const key = `${size.width}x${size.height}`
|
|
40
|
+
renderer.addRootNode(key, createElement(ReactFragment, null, content))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const rendered = renderer.render()
|
|
44
|
+
|
|
45
|
+
// Extract variant keys (everything except v, s, e which are metadata)
|
|
46
|
+
const variantsMap: Record<string, any> = {}
|
|
47
|
+
const metadataKeys = ['v', 's', 'e']
|
|
48
|
+
|
|
49
|
+
for (const key of Object.keys(rendered)) {
|
|
50
|
+
if (!metadataKeys.includes(key)) {
|
|
51
|
+
variantsMap[key] = rendered[key]
|
|
52
|
+
delete rendered[key]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Add variants as a nested object (expected by Kotlin parser)
|
|
57
|
+
rendered.variants = variantsMap
|
|
58
|
+
|
|
59
|
+
return rendered
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Renders Android widget variants to a JSON string.
|
|
64
|
+
*/
|
|
65
|
+
export const renderAndroidWidgetToString = (
|
|
66
|
+
variants: AndroidWidgetVariants,
|
|
67
|
+
options?: AndroidWidgetRenderOptions
|
|
68
|
+
): string => {
|
|
69
|
+
return JSON.stringify(renderAndroidWidgetToJson(variants, options))
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Renders Android JSX to JSON for VoltraView component.
|
|
74
|
+
*/
|
|
75
|
+
export const renderAndroidViewToJson = (
|
|
76
|
+
children: ReactNode,
|
|
77
|
+
_options?: AndroidWidgetRenderOptions
|
|
78
|
+
): Record<string, any> => {
|
|
79
|
+
const renderer = createVoltraRenderer(androidComponentRegistry)
|
|
80
|
+
|
|
81
|
+
renderer.addRootNode('content', createElement(ReactFragment, null, children))
|
|
82
|
+
|
|
83
|
+
const rendered = renderer.render()
|
|
84
|
+
const node = rendered.content
|
|
85
|
+
|
|
86
|
+
delete rendered.content
|
|
87
|
+
rendered.variants = { content: node }
|
|
88
|
+
|
|
89
|
+
return rendered
|
|
90
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Size breakpoint for Android widgets.
|
|
5
|
+
* Represents width x height in dp.
|
|
6
|
+
*/
|
|
7
|
+
export type AndroidWidgetSize = {
|
|
8
|
+
width: number
|
|
9
|
+
height: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A single size variant with its breakpoint and content.
|
|
14
|
+
*/
|
|
15
|
+
export type AndroidWidgetSizeVariant = {
|
|
16
|
+
size: AndroidWidgetSize
|
|
17
|
+
content: ReactNode
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Information about an active widget instance on Android
|
|
22
|
+
*/
|
|
23
|
+
export type WidgetInfo = {
|
|
24
|
+
/** The name (ID) of the widget as defined in the config plugin */
|
|
25
|
+
name: string
|
|
26
|
+
/** The unique ID for this widget instance (required for updates) */
|
|
27
|
+
widgetId: number
|
|
28
|
+
/** The class name of the provider (e.g., ".WeatherWidget") */
|
|
29
|
+
providerClassName: string
|
|
30
|
+
/** Current labeling associated with the widget */
|
|
31
|
+
label: string
|
|
32
|
+
/** Dimensions in dp as reported by the system */
|
|
33
|
+
width: number
|
|
34
|
+
height: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Widget variants using size-based breakpoints.
|
|
39
|
+
* Android picks the best matching variant based on widget dimensions.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* const variants: AndroidWidgetVariants = [
|
|
44
|
+
* { size: { width: 150, height: 100 }, content: <SmallWidget /> },
|
|
45
|
+
* { size: { width: 150, height: 200 }, content: <TallWidget /> },
|
|
46
|
+
* { size: { width: 215, height: 100 }, content: <WideWidget /> },
|
|
47
|
+
* ]
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export type AndroidWidgetVariants = AndroidWidgetSizeVariant[]
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Options for updating an Android widget
|
|
54
|
+
*/
|
|
55
|
+
export type UpdateAndroidWidgetOptions = {
|
|
56
|
+
/**
|
|
57
|
+
* URL to open when the widget is tapped.
|
|
58
|
+
* Can be a full URL (e.g., "myapp://screen/details")
|
|
59
|
+
* or a path that will be prefixed with your app's URL scheme.
|
|
60
|
+
*/
|
|
61
|
+
deepLinkUrl?: string
|
|
62
|
+
}
|