Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/empty-turkeys-strive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@lynx-js/web-mainthread-apis": patch
"@lynx-js/web-constants": patch
---

feat: support main thread invoke ui method
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { I18nResourceTranslationOptions } from './index.js';
import type { MainThreadLynx } from './MainThreadLynx.js';
import type { ProcessDataCallback } from './ProcessDataCallback.js';
import type { UpdateDataOptions } from './UpdateDataOptions.js';
import type { InvokeCallbackRes } from './NativeApp.js';

type ElementPAPIEventHandler =
| string
Expand Down Expand Up @@ -317,6 +318,13 @@ export type QueryComponentPAPI = (
};
}) => void,
) => null;
export type InvokeUIMethodPAPI = (
element: unknown,
method: string,
params: object,
callback: (result: InvokeCallbackRes) => void,
) => void;

export interface ElementPAPIs {
__ElementFromBinary: ElementFromBinaryPAPI;

Expand Down Expand Up @@ -381,6 +389,7 @@ export interface ElementPAPIs {
_subTree?: unknown,
options?: FlushElementTreeOptions,
) => void;
__InvokeUIMethod: InvokeUIMethodPAPI;
}

export interface MainThreadGlobalThis extends ElementPAPIs {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import {
type JSRealm,
type QueryComponentPAPI,
lynxEntryNameAttribute,
ErrorCode,
} from '@lynx-js/web-constants';
import { createMainThreadLynx } from './createMainThreadLynx.js';
import {
Expand Down Expand Up @@ -816,6 +817,46 @@ export function createMainThreadGlobalThis(
_I18nResourceTranslation: callbacks._I18nResourceTranslation,
_AddEventListener: () => {},
renderPage: undefined,
__InvokeUIMethod: (element, method, params, callback) => {
try {
if (method === 'boundingClientRect') {
const rect = (element as HTMLElement).getBoundingClientRect();
callback({
code: ErrorCode.SUCCESS,
data: {
id: (element as HTMLElement).id,
width: rect.width,
height: rect.height,
left: rect.left,
right: rect.right,
top: rect.top,
bottom: rect.bottom,
},
});
return;
}
if (typeof (element as any)[method] === 'function') {
const data = (element as any)[method](params);
callback({
code: ErrorCode.SUCCESS,
data,
});
return;
}
callback({
code: ErrorCode.METHOD_NOT_FOUND,
});
} catch (e) {
console.error(
`[lynx-web] invokeUIMethod: apply method failed with`,
e,
element,
);
callback({
code: ErrorCode.PARAM_INVALID,
});
}
},
Comment thread
Sherry-hue marked this conversation as resolved.
};
Object.assign(mtsRealm.globalWindow, mtsGlobalThis);
Object.defineProperty(mtsRealm.globalWindow, 'renderPage', {
Expand Down
16 changes: 16 additions & 0 deletions packages/web-platform/web-tests/tests/react.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,22 @@ test.describe('reactlynx3 tests', () => {
},
);

test('basic-ref-main-invoke-ui-method', async ({ page }, { title }) => {
await goto(page, title);
await wait(100);
const scrollView = page.locator('#scroll-view');
const scrollTopBefore = await scrollView.evaluate((node) =>
node.scrollTop
);
expect(scrollTopBefore).toBe(0);
await page.locator('#target').click();
await wait(3000);
const scrollTopAfter = await scrollView.evaluate((node) =>
node.scrollTop
);
expect(scrollTopAfter).toBeGreaterThan(100);
});

// lazy component
test(
'basic-lazy-component',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { useMainThreadRef, root } from '@lynx-js/react';

export const ScrollItem = (props) => {
// Calculate gradient angle based on index to create wave effect
const angle = 90 + 6 * props.index;

return (
<view
style={{
width: props.width,
height: props.height,
background:
`linear-gradient(${angle}deg, rgb(255,53,26), rgb(0,235,235))`,
margin: '3px',
borderRadius: '10px',
}}
>
</view>
);
};

export const App = () => {
const scrollRef = useMainThreadRef(null);

const handleTap = () => {
'main thread';
scrollRef.current?.invoke('autoScroll', {
rate: 120,
start: true,
});
};

return (
<view
style={{
width: '100%',
height: '100%',
padding: '10px',
display: 'linear',
marginTop: '20px',
}}
>
<view main-thread:bindtap={handleTap} id='target'>
<text
style={{
fontSize: '20px',
height: '40px',
paddingLeft: '10px',
marginTop: '10px',
}}
>
Tap me to enable auto-scroll
</text>
</view>
<scroll-view
id='scroll-view'
main-thread:ref={scrollRef}
scroll-orientation='vertical'
style={{ width: '100%', height: '300px', paddingLeft: '5px' }}
>
{Array.from({ length: 5 }).map((item, index) => (
<ScrollItem width='calc(100% - 10px)' height='100px' index={index} />
))}
</scroll-view>
</view>
);
};
root.render(<App />);
Loading