@use-voltra/android 2.0.0 → 2.1.1

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.
Files changed (81) hide show
  1. package/build/commonjs/index.js +20 -0
  2. package/build/commonjs/index.js.map +1 -1
  3. package/build/module/index.js +6 -0
  4. package/build/module/index.js.map +1 -1
  5. package/build/typescript/commonjs/index.d.ts +3 -0
  6. package/build/typescript/commonjs/index.d.ts.map +1 -1
  7. package/build/typescript/module/index.d.ts +3 -0
  8. package/build/typescript/module/index.d.ts.map +1 -1
  9. package/package.json +3 -2
  10. package/src/dynamic-colors.ts +33 -0
  11. package/src/index.ts +99 -0
  12. package/src/internal.ts +2 -0
  13. package/src/jsx/AreaMark.tsx +16 -0
  14. package/src/jsx/BarMark.tsx +18 -0
  15. package/src/jsx/Box.tsx +5 -0
  16. package/src/jsx/Button.tsx +5 -0
  17. package/src/jsx/Chart.tsx +123 -0
  18. package/src/jsx/CheckBox.tsx +5 -0
  19. package/src/jsx/CircleIconButton.tsx +14 -0
  20. package/src/jsx/CircularProgressIndicator.tsx +7 -0
  21. package/src/jsx/Column.tsx +5 -0
  22. package/src/jsx/FilledButton.tsx +14 -0
  23. package/src/jsx/Image.tsx +21 -0
  24. package/src/jsx/LazyColumn.tsx +5 -0
  25. package/src/jsx/LazyVerticalGrid.tsx +45 -0
  26. package/src/jsx/LineMark.tsx +17 -0
  27. package/src/jsx/LinearProgressIndicator.tsx +7 -0
  28. package/src/jsx/OutlineButton.tsx +14 -0
  29. package/src/jsx/PointMark.tsx +16 -0
  30. package/src/jsx/RadioButton.tsx +5 -0
  31. package/src/jsx/Row.tsx +5 -0
  32. package/src/jsx/RuleMark.tsx +15 -0
  33. package/src/jsx/Scaffold.tsx +5 -0
  34. package/src/jsx/SectorMark.tsx +17 -0
  35. package/src/jsx/Spacer.tsx +5 -0
  36. package/src/jsx/SquareIconButton.tsx +14 -0
  37. package/src/jsx/Switch.tsx +5 -0
  38. package/src/jsx/Text.tsx +10 -0
  39. package/src/jsx/TitleBar.tsx +18 -0
  40. package/src/jsx/baseProps.tsx +13 -0
  41. package/src/jsx/chart-types.ts +2 -0
  42. package/src/jsx/createVoltraComponent.ts +2 -0
  43. package/src/jsx/index.ts +1 -0
  44. package/src/jsx/primitives.ts +28 -0
  45. package/src/jsx/props/.generated +15 -0
  46. package/src/jsx/props/AndroidBox.ts +19 -0
  47. package/src/jsx/props/AndroidButton.ts +10 -0
  48. package/src/jsx/props/AndroidChart.ts +20 -0
  49. package/src/jsx/props/AndroidCheckBox.ts +20 -0
  50. package/src/jsx/props/AndroidCircleIconButton.ts +18 -0
  51. package/src/jsx/props/AndroidCircularProgressIndicator.ts +10 -0
  52. package/src/jsx/props/AndroidColumn.ts +12 -0
  53. package/src/jsx/props/AndroidFilledButton.ts +20 -0
  54. package/src/jsx/props/AndroidImage.ts +26 -0
  55. package/src/jsx/props/AndroidLazyColumn.ts +10 -0
  56. package/src/jsx/props/AndroidLazyVerticalGrid.ts +12 -0
  57. package/src/jsx/props/AndroidLinearProgressIndicator.ts +14 -0
  58. package/src/jsx/props/AndroidOutlineButton.ts +18 -0
  59. package/src/jsx/props/AndroidRadioButton.ts +22 -0
  60. package/src/jsx/props/AndroidRow.ts +12 -0
  61. package/src/jsx/props/AndroidScaffold.ts +12 -0
  62. package/src/jsx/props/AndroidSpacer.ts +7 -0
  63. package/src/jsx/props/AndroidSquareIconButton.ts +18 -0
  64. package/src/jsx/props/AndroidSwitch.ts +24 -0
  65. package/src/jsx/props/AndroidText.ts +14 -0
  66. package/src/jsx/props/AndroidTitleBar.ts +18 -0
  67. package/src/live-update/renderer.ts +36 -0
  68. package/src/live-update/types.ts +44 -0
  69. package/src/ongoing-notification/components.tsx +49 -0
  70. package/src/ongoing-notification/renderer.ts +360 -0
  71. package/src/ongoing-notification/types.ts +199 -0
  72. package/src/payload/component-ids.ts +89 -0
  73. package/src/preload-utils.ts +78 -0
  74. package/src/renderer/index.ts +6 -0
  75. package/src/renderer/renderer.ts +25 -0
  76. package/src/server.ts +19 -0
  77. package/src/styles/types.ts +114 -0
  78. package/src/types.ts +38 -0
  79. package/src/utils/index.ts +0 -0
  80. package/src/widgets/renderer.ts +90 -0
  81. package/src/widgets/types.ts +62 -0
@@ -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,6 @@
1
+ export {
2
+ type ComponentRegistry,
3
+ createVoltraRenderer,
4
+ renderAndroidVariantToJson,
5
+ VOLTRA_PAYLOAD_VERSION,
6
+ } from './renderer.js'
@@ -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
+ }