diff --git a/.changeset/wide-views-admire.md b/.changeset/wide-views-admire.md new file mode 100644 index 0000000000..ddaf0a2805 --- /dev/null +++ b/.changeset/wide-views-admire.md @@ -0,0 +1,17 @@ +--- +"@lynx-js/web-elements": minor +--- + +feat: integrate the LinearContainer Compat plugin + +**This is a BREAKING CHANGE** + +Now we integrated the `LinearCompat` into @lynx-js/web-elements. Developers can safely remove the following imports: + +```js +import '@lynx-js/web-elements/compat/LinearContainer'; +``` + +```js +import '@lynx-js/web-elements-compat/LinearContainer'; +``` diff --git a/packages/web-platform/web-core-wasm-e2e/tests/utils.ts b/packages/web-platform/web-core-wasm-e2e/tests/utils.ts deleted file mode 100644 index d051ae3b0c..0000000000 --- a/packages/web-platform/web-core-wasm-e2e/tests/utils.ts +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. - -// Copyright 2024 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. -import type { CDPSession } from '@playwright/test'; - -export const swipe = async ( - cdpSession: CDPSession, - options: { - x: number; - y: number; - xDistance: number; - yDistance: number; - speed?: number; - steps?: number; - }, -): Promise => { - const { x, y, yDistance, xDistance, speed = 300, steps = 10 } = options; - const xStepDistance = xDistance / steps; - const yStepDistance = yDistance / steps; - await cdpSession.send('Input.dispatchTouchEvent', { - type: 'touchStart', - touchPoints: [{ - x, - y, - }], - }); - for (let ii = 0; ii < steps + 2; ii++) { - await cdpSession.send('Input.dispatchTouchEvent', { - type: 'touchMove', - touchPoints: [{ - x: x + (ii + 1) * xStepDistance, - y: y + (ii + 1) * yStepDistance, - }], - }); - } - await cdpSession.send('Input.dispatchTouchEvent', { - type: 'touchEnd', - touchPoints: [{ - x: x + xDistance, - y: y + yDistance, - }], - }); -}; - -export const dragAndHold = async ( - cdpSession: CDPSession, - options: { - x: number; - y: number; - xDistance: number; - yDistance: number; - }, -): Promise<() => Promise> => { - const { x, y, xDistance, yDistance } = options; - await cdpSession.send('Input.dispatchTouchEvent', { - type: 'touchStart', - touchPoints: [ - { - x, - y, - }, - ], - }); - await cdpSession.send('Input.dispatchTouchEvent', { - type: 'touchMove', - touchPoints: [{ x, y }], - }); - await cdpSession.send('Input.dispatchTouchEvent', { - type: 'touchMove', - touchPoints: [{ x: x + xDistance, y: y + yDistance }], - }); - const touchEnd = async () => { - await cdpSession.send('Input.dispatchTouchEvent', { - type: 'touchEnd', - touchPoints: [{ x: x + xDistance, y: y + yDistance }], - }); - }; - return touchEnd; -}; diff --git a/packages/web-platform/web-core-wasm-e2e/tests/web-core.test.ts b/packages/web-platform/web-core-wasm-e2e/tests/web-core.test.ts index b2932837bb..be31bc4b7e 100644 --- a/packages/web-platform/web-core-wasm-e2e/tests/web-core.test.ts +++ b/packages/web-platform/web-core-wasm-e2e/tests/web-core.test.ts @@ -1,12 +1,16 @@ // Copyright 2024 The Lynx Authors. All rights reserved. // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. -// @ts-nocheck -import { test, expect } from './coverage-fixture.js'; +import { test, expect } from '@lynx-js/playwright-fixtures'; +import type { LynxViewElement } from '@lynx-js/web-core-wasm/client'; import type { Page, Worker } from '@playwright/test'; -const ENABLE_MULTI_THREAD = !!process.env.ENABLE_MULTI_THREAD; -const isSSR = !!process.env['ENABLE_SSR']; +declare global { + // eslint-disable-next-line no-var + var runtime: any; + // eslint-disable-next-line no-var + var __lynx_worker_type: string; +} const wait = async (ms: number) => { await new Promise((resolve) => { @@ -15,40 +19,15 @@ const wait = async (ms: number) => { }; const goto = async (page: Page, title?: string) => { - let url = '/web-core.html'; - if (title) { - url += `?casename=${title}`; - } - - await page.goto(url, { + await page.goto('/?resourceName=web-core.main-thread.json', { waitUntil: 'load', }); await wait(500); }; -async function getMainThreadWorker( - page: Page, -): Promise { - await wait(100); - if (!ENABLE_MULTI_THREAD) { - return page; - } else { - for (const i of page.workers()) { - const isActive = await i.evaluate(() => { - return globalThis.runtime !== undefined - && globalThis.__lynx_worker_type === 'main'; - }); - - if (isActive) { - return i; - } - } - } -} - async function getBackgroundThreadWorker( page: Page, -): Promise { +): Promise { await wait(100); for (const i of page.workers()) { const isActive = await i.evaluate(() => { @@ -60,16 +39,15 @@ async function getBackgroundThreadWorker( return i; } } + throw new Error('background thread worker not found'); } test.describe('web core tests', () => { - test.skip(isSSR, 'not support ssr'); test('selectComponent', async ({ page, browserName }) => { // firefox not support test.skip(browserName === 'firefox'); await goto(page); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { + await page.evaluate(() => { globalThis.runtime.renderPage = () => { const root = globalThis.runtime.__CreatePage('0', '0', {}); const element = globalThis.runtime.__CreateElement('view', '0', {}); @@ -88,6 +66,7 @@ test.describe('web core tests', () => { globalThis.runtime.__AppendElement(element, component); }; }); + await wait(200); const backWorker = await getBackgroundThreadWorker(page); const isSuccess = await backWorker.evaluate(() => { return new Promise(resolve => { @@ -95,7 +74,7 @@ test.describe('web core tests', () => { 'card', '.wrapper', true, - (ids) => { + (ids: unknown) => { if (Array.isArray(ids) && ids[0] === '0-13826000') { resolve(true); } @@ -108,12 +87,11 @@ test.describe('web core tests', () => { }); test('lynx.requireModuleAsync', async ({ page, browserName }) => { test.skip( - browserName === 'firefox' && ENABLE_MULTI_THREAD, + browserName === 'firefox', 'firefox flaky', ); await goto(page); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { + await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; }); const worker = await getBackgroundThreadWorker(page); @@ -121,7 +99,7 @@ test.describe('web core tests', () => { const { promise, resolve } = Promise.withResolvers(); globalThis.runtime.lynx.requireModuleAsync( 'manifest-chunk.js', - (_, exports) => { + (_: unknown, exports: string) => { resolve(exports); }, ); @@ -131,12 +109,11 @@ test.describe('web core tests', () => { }); test('lynx.requireModuleAsync-2', async ({ page, browserName }) => { test.skip( - browserName === 'firefox' && ENABLE_MULTI_THREAD, + browserName === 'firefox', 'firefox flaky', ); await goto(page); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { + await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; }); const worker = await getBackgroundThreadWorker(page); @@ -145,13 +122,13 @@ test.describe('web core tests', () => { const chunk2 = Promise.withResolvers(); globalThis.runtime.lynx.requireModuleAsync( 'manifest-chunk.js', - (_, exports) => { + (_: unknown, exports: string) => { chunk1.resolve(exports); }, ); globalThis.runtime.lynx.requireModuleAsync( 'manifest-chunk2.js', - (_, exports) => { + (_: unknown, exports: string) => { chunk2.resolve(exports); }, ); @@ -162,12 +139,11 @@ test.describe('web core tests', () => { }); test('lynx.requireModule+sync', async ({ page, browserName }) => { test.skip( - browserName === 'firefox' && ENABLE_MULTI_THREAD, + browserName === 'firefox', 'firefox flaky', ); await goto(page); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { + await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; }); const worker = await getBackgroundThreadWorker(page); @@ -176,7 +152,7 @@ test.describe('web core tests', () => { const chunk2 = Promise.withResolvers(); globalThis.runtime.lynx.requireModuleAsync( 'manifest-chunk.js', - (_, exports) => { + (_: unknown, exports: string) => { chunk1.resolve(exports); }, ); @@ -191,11 +167,10 @@ test.describe('web core tests', () => { test('loadLepusChunk', async ({ page, browserName }) => { await goto(page); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { + await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; }); - const [success, fail] = await mainWorker!.evaluate(async () => { + const [success, fail] = await page.evaluate(async () => { return [ globalThis.runtime.__LoadLepusChunk('manifest-chunk2.js'), globalThis.runtime.__LoadLepusChunk('manifest-chunk8.js'), @@ -209,8 +184,7 @@ test.describe('web core tests', () => { // firefox dose not support this. test.skip(browserName === 'firefox'); await goto(page); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { + await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; }); await wait(3000); @@ -224,8 +198,7 @@ test.describe('web core tests', () => { }); test('registerDataProcessor-as-global-var-update', async ({ page, browserName }) => { await goto(page); - const mainWorker = await getMainThreadWorker(page); - const registerDataProcessor = await mainWorker.evaluate(() => { + const registerDataProcessor = await page.evaluate(() => { return globalThis.runtime.registerDataProcessor; }); expect(registerDataProcessor).toBe('pass'); @@ -236,8 +209,7 @@ test.describe('web core tests', () => { // firefox dose not support this. test.skip(browserName === 'firefox'); await goto(page); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { + await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; }); const backgroundWorker = await getBackgroundThreadWorker(page); @@ -284,8 +256,7 @@ test.describe('web core tests', () => { // firefox dose not support this. test.skip(browserName === 'firefox'); await goto(page); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { + await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; }); await wait(3000); @@ -293,15 +264,16 @@ test.describe('web core tests', () => { let successCallback = false; let successCallback2 = false; await page.on('console', async (message) => { - if (message.text() === 'green') { + if (message.text().includes('green')) { successCallback = true; } - if (message.text() === 'LYNX-VIEW') { + if (message.text().includes('LYNX-VIEW')) { successCallback2 = true; } }); await backWorker.evaluate(() => { const nativeApp = globalThis.runtime.lynx.getNativeApp(); + // @ts-expect-error const colorStarter = globalThis[`napiLoaderOnRT${nativeApp.id}`].load( 'color_environment', ); @@ -315,8 +287,7 @@ test.describe('web core tests', () => { // firefox dose not support this. test.skip(browserName === 'firefox'); await goto(page); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { + await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; }); await wait(3000); @@ -324,22 +295,23 @@ test.describe('web core tests', () => { let successCallback = false; let successCallback2 = false; await page.on('console', async (message) => { - if (message.text() === 'green') { + if (message.text().includes('green')) { successCallback = true; } - if (message.text() === 'LYNX-VIEW') { + if (message.text().includes('LYNX-VIEW')) { successCallback2 = true; } }); await backWorker.evaluate(() => { const nativeApp = globalThis.runtime.lynx.getNativeApp(); + // @ts-expect-error const colorStarter = globalThis[`napiLoaderOnRT${nativeApp.id}`].load( 'color_environment', ); const engine = new colorStarter.ColorEngine(); engine.getColor(); }); - await wait(100); + await wait(500); expect(successCallback).toBeTruthy(); expect(successCallback2).toBeTruthy(); }); @@ -347,8 +319,8 @@ test.describe('web core tests', () => { // firefox dose not support this. test.skip(browserName === 'firefox'); await goto(page); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { + await wait(200); + await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; }); await wait(3000); @@ -361,14 +333,15 @@ test.describe('web core tests', () => { }); await backWorker.evaluate(() => { const nativeApp = globalThis.runtime.lynx.getNativeApp(); - const eventMethod = globalThis[`napiLoaderOnRT${nativeApp.id}`].load( - 'event_method', - ); + const eventMethod = (globalThis as any)[`napiLoaderOnRT${nativeApp.id}`] + .load( + 'event_method', + ); eventMethod.bindEvent(); }); await wait(1000); await page.evaluate(() => { - document.querySelector('lynx-view')?.click(); + (document.querySelector('lynx-view') as HTMLElement)?.click(); }); await wait(1000); expect(successDispatchNapiModule).toBeTruthy(); @@ -377,8 +350,7 @@ test.describe('web core tests', () => { // firefox dose not support this. test.skip(browserName === 'firefox'); await goto(page); - const mainWorker = await getMainThreadWorker(page); - const success = await mainWorker.evaluate(() => { + const success = await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; if ( JSON.stringify(globalThis.runtime._I18nResourceTranslation({ @@ -398,22 +370,18 @@ test.describe('web core tests', () => { test.skip(browserName === 'firefox'); await goto(page); let success = false; - await page.on('console', async (msg) => { - const event = await msg.args()[0]?.evaluate((e) => { - return { - type: e.type, - channel: e.detail?.channel, - }; - }); - if (!event || event.type !== 'i18nResourceMissed') { - return; - } - if (event.channel === '2') { - success = true; - } + await page.evaluate(() => { + (document.querySelector('lynx-view') as LynxViewElement).addEventListener( + 'i18nResourceMissed', + (event) => { + // @ts-expect-error + if (event.detail.channel === '2') { + success = true; + } + }, + ); }); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { + await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; globalThis.runtime._I18nResourceTranslation({ locale: 'en', @@ -421,15 +389,17 @@ test.describe('web core tests', () => { fallback_url: '', }); }); - await wait(2000); + await wait(500); + success = await page.evaluate(() => { + return success; + }); expect(success).toBeTruthy(); }); test('api-update-i18n-resources', async ({ page, browserName }) => { // firefox dose not support this. test.skip(browserName === 'firefox'); await goto(page); - const mainWorker = await getMainThreadWorker(page); - const first = await mainWorker.evaluate(() => { + const first = await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; if ( globalThis.runtime._I18nResourceTranslation({ @@ -443,7 +413,7 @@ test.describe('web core tests', () => { }); await wait(500); await page.evaluate(() => { - document.querySelector('lynx-view').updateI18nResources([ + (document.querySelector('lynx-view') as any)?.updateI18nResources([ { options: { locale: 'en', @@ -473,7 +443,7 @@ test.describe('web core tests', () => { }); }); await wait(500); - const second = await mainWorker.evaluate(() => { + const second = await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; if ( JSON.stringify(globalThis.runtime._I18nResourceTranslation({ @@ -493,8 +463,7 @@ test.describe('web core tests', () => { // firefox dose not support this. test.skip(browserName === 'firefox'); await goto(page); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { + await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; }); await wait(500); @@ -503,7 +472,7 @@ test.describe('web core tests', () => { globalThis.runtime.lynx.getNativeLynx().getI18nResource() === undefined ); await wait(500); - await mainWorker?.evaluate(() => { + await page.evaluate(() => { globalThis.runtime._I18nResourceTranslation({ locale: 'en', channel: '1', @@ -521,8 +490,7 @@ test.describe('web core tests', () => { // firefox dose not support this. test.skip(browserName === 'firefox'); await goto(page); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { + await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; }); await wait(500); @@ -532,7 +500,7 @@ test.describe('web core tests', () => { ); await wait(500); await page.evaluate(() => { - document.querySelector('lynx-view').updateI18nResources([ + (document.querySelector('lynx-view') as any)?.updateI18nResources([ { options: { locale: 'en', @@ -573,8 +541,7 @@ test.describe('web core tests', () => { // firefox dose not support this. test.skip(browserName === 'firefox'); await goto(page); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { + await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; }); await wait(500); @@ -594,7 +561,7 @@ test.describe('web core tests', () => { ); }); await wait(500); - await mainWorker?.evaluate(() => { + await page.evaluate(() => { globalThis.runtime._I18nResourceTranslation({ locale: 'en', channel: '1', @@ -608,8 +575,7 @@ test.describe('web core tests', () => { // firefox dose not support this. test.skip(browserName === 'firefox'); await goto(page); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { + await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; }); await wait(500); @@ -630,7 +596,7 @@ test.describe('web core tests', () => { }); await wait(500); await page.evaluate(() => { - document.querySelector('lynx-view').updateI18nResources([ + (document.querySelector('lynx-view') as any)?.updateI18nResources([ { options: { locale: 'en', @@ -662,40 +628,11 @@ test.describe('web core tests', () => { await wait(500); expect(success).toBeTruthy(); }); - test('decode-css-in-js-warn', async ({ page, browserName }) => { - // firefox not support - test.skip(browserName === 'firefox'); - await goto(page, 'enable-css-selector-false'); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { - globalThis.runtime.renderPage = () => { - const root = globalThis.runtime.__CreatePage('0', '0', {}); - const container = globalThis.runtime.__CreateElement('view', '0', {}); - globalThis.runtime.__SetAttribute(container, 'l-css-id', '-1'); - globalThis.runtime.__SetAttribute( - container, - 'style', - 'width: 100px;height: 100px; background-color: red', - ); - globalThis.runtime.__AppendElement(root, container); - globalThis.runtime.__AddClass(container, 'target'); - }; - }); - await wait(1000); - const height = await page.evaluate(() => - getComputedStyle(document.querySelector('lynx-view')).getPropertyValue( - 'height', - ) - ); - await wait(500); - expect(height).toBe('100px'); - }); test('source-map-release', async ({ page, browserName }) => { // firefox not support test.skip(browserName === 'firefox'); await goto(page); - const mainWorker = await getMainThreadWorker(page); - await mainWorker.evaluate(() => { + await page.evaluate(() => { globalThis.runtime.renderPage = () => {}; }); const backWorker = await getBackgroundThreadWorker(page); diff --git a/packages/web-platform/web-core-wasm/css/in_shadow.css b/packages/web-platform/web-core-wasm/css/in_shadow.css index 2bf8528d8a..59b0ea52f1 100644 --- a/packages/web-platform/web-core-wasm/css/in_shadow.css +++ b/packages/web-platform/web-core-wasm/css/in_shadow.css @@ -1,9 +1,10 @@ -@import url("./linear.css"); -@import url("@lynx-js/web-elements/elements.css"); +@import url("@lynx-js/web-elements/index.css"); + [lynx-default-display-linear="false"] * { --lynx-display: flex; --lynx-display-toggle: var(--lynx-display-flex); } + [lynx-default-overflow-visible="true"] x-view { overflow: visible; } diff --git a/packages/web-platform/web-core-wasm/css/linear.css b/packages/web-platform/web-core-wasm/css/linear.css deleted file mode 100644 index e6716721b8..0000000000 --- a/packages/web-platform/web-core-wasm/css/linear.css +++ /dev/null @@ -1,167 +0,0 @@ -div * { - display: flex; - box-sizing: border-box; - border-width: 0px; - position: relative; - overflow: clip; - min-width: 0; - min-height: 0; - border-style: solid; - scrollbar-width: none; -} - -x-view::--webkit-scrollbar { - display: none; -} - -/** - * only enable this toggle logic for those container elements - */ -div * { - /* - --lynx-display-toggle is compile-time generated. - */ - --lynx-display-toggle: var(--lynx-display-linear); - --lynx-display-linear: var(--lynx-display-toggle,); - --lynx-display-flex: var(--lynx-display-toggle,); - /* - --lynx-linear-orientation-toggle is compile-time generated. - */ - --lynx-linear-orientation-toggle: var(--lynx-linear-orientation-vertical); - --lynx-linear-orientation-horizontal: var(--lynx-linear-orientation-toggle,); - --lynx-linear-orientation-vertical: var(--lynx-linear-orientation-toggle,); - --lynx-linear-orientation-horizontal-reverse: var( - --lynx-linear-orientation-toggle, - ); - --lynx-linear-orientation-vertical-reverse: var( - --lynx-linear-orientation-toggle, - ); - - --linear-flex-direction: var(--lynx-linear-orientation-horizontal, row) var( - --lynx-linear-orientation-vertical, - column - ) var(--lynx-linear-orientation-horizontal-reverse, row-reverse) var( - --lynx-linear-orientation-vertical-reverse, - column-reverse - ); - --linear-justify-content: var( - --lynx-linear-orientation-horizontal, - var(--justify-content-row) - ) var(--lynx-linear-orientation-vertical, var(--justify-content-column)) var( - --lynx-linear-orientation-horizontal-reverse, - var(--justify-content-row-reverse) - ) var( - --lynx-linear-orientation-vertical-reverse, - var(--justify-content-column-reverse) - ); -} -div * { - flex-wrap: var(--lynx-display-linear, nowrap) - var( - --lynx-display-flex, - var(--flex-wrap) - ); - flex-direction: var(--lynx-display-linear, var(--linear-flex-direction)) - var( - --lynx-display-flex, - var(--flex-direction) - ); - justify-content: var(--lynx-display-linear, var(--linear-justify-content)); -} - -/** For @container - * - * when the chromuim version is less than 116.0.5806.0, the following code will crash: - * ``` - * -
-
-
- - * ``` - * it fixed in 116.0.5806.0, detail: https://issues.chromium.org/issues/40270007 - * - * so we limit this feature to chrome 117, safari 18, firefox no: - * rex unit: chrome 111, safari 17.2, firefox no - * https://developer.mozilla.org/en-US/docs/Web/CSS/length - * transition-behavior:allow-discrete: chrome 117, safari 18, firefox 125 - * https://developer.mozilla.org/en-US/docs/Web/CSS/transition-behavior - * https://caniuse.com/mdn-css_properties_display_is_transitionable - * - * update this once firefox supports this. - * - * If you want to be fully compatible with chrome below 117, you need to use a plugin @lynx-js/web-elements-compat. - */ -@supports (content-visibility: auto) and - (transition-behavior: allow-discrete) and (width: 1rex) { - @container style(--lynx-display: linear) { - div * { - /* - `--lynx-linear-weight-sum` - 0 -> 1 - -> - */ - flex-shrink: 0; - /* The following `calc` and `clamp` logic ensures that if - `--lynx-linear-weight-sum` is zero, it defaults to 1. This prevents - division by zero and ensures consistent behavior. */ - flex-grow: calc( - var(--lynx-linear-weight) / - calc( - var(--lynx-linear-weight-sum) + - ( - 1 - clamp(0, var(--lynx-linear-weight-sum) * 999999, 1) - ) - ) - ); - flex-basis: var(--lynx-linear-weight-basis); - } - } - - @container not style(--lynx-display: linear) { - div * { - flex: var( - --flex, - var(--flex-grow) var(--flex-shrink) var(--flex-basis) - ); - } - } - - @container style(--lynx-display: linear) and - (style(--lynx-linear-orientation: vertical) or - style(--lynx-linear-orientation: vertical-reverse)) { - div * { - align-self: var(--align-self-column); - } - } - - @container style(--lynx-display: linear) and - (style(--lynx-linear-orientation: horizontal) or - style(--lynx-linear-orientation: horizontal-reverse)) { - div * { - align-self: var(--align-self-row); - } - } -} diff --git a/packages/web-platform/web-elements/elements.css b/packages/web-platform/web-elements/elements.css deleted file mode 100644 index 05e40b99f0..0000000000 --- a/packages/web-platform/web-elements/elements.css +++ /dev/null @@ -1,19 +0,0 @@ -/* Copyright 2024 The Lynx Authors. All rights reserved. - Licensed under the Apache License Version 2.0 that can be found in the - LICENSE file in the root directory of this source tree. - */ -@import url("./src/elements/LynxWrapper/lynx-wrapper.css"); -@import url("./src/elements/XText/x-text.css"); -@import url("./src/elements/ScrollView/scroll-view.css"); -@import url("./src/elements/XFoldViewNg/x-foldview-ng.css"); -@import url("./src/elements/XViewpagerNg/x-viewpager-ng.css"); -@import url("./src/elements/XCanvas/x-canvas.css"); -@import url("./src/elements/XSvg/x-svg.css"); -@import url("./src/elements/XImage/x-image.css"); -@import url("./src/elements/XInput/x-input.css"); -@import url("./src/elements/XOverlayNg/x-overlay-ng.css"); -@import url("./src/elements/XRefreshView/x-refresh-view.css"); -@import url("./src/elements/XSwiper/x-swiper.css"); -@import url("./src/elements/XTextarea/x-textarea.css"); -@import url("./src/elements/XList/x-list.css"); -@import url("./src/elements/XWebView/x-webview.css"); diff --git a/packages/web-platform/web-elements/index.css b/packages/web-platform/web-elements/index.css index b98443c4f6..dcdf435448 100644 --- a/packages/web-platform/web-elements/index.css +++ b/packages/web-platform/web-elements/index.css @@ -2,5 +2,20 @@ Licensed under the Apache License Version 2.0 that can be found in the LICENSE file in the root directory of this source tree. */ +@import url("./src/compat/LinearContainer/linear-compat.css"); @import url("./src/elements/common-css/linear.css"); -@import url("./elements.css"); +@import url("./src/elements/LynxWrapper/lynx-wrapper.css"); +@import url("./src/elements/XText/x-text.css"); +@import url("./src/elements/ScrollView/scroll-view.css"); +@import url("./src/elements/XFoldViewNg/x-foldview-ng.css"); +@import url("./src/elements/XViewpagerNg/x-viewpager-ng.css"); +@import url("./src/elements/XCanvas/x-canvas.css"); +@import url("./src/elements/XSvg/x-svg.css"); +@import url("./src/elements/XImage/x-image.css"); +@import url("./src/elements/XInput/x-input.css"); +@import url("./src/elements/XOverlayNg/x-overlay-ng.css"); +@import url("./src/elements/XRefreshView/x-refresh-view.css"); +@import url("./src/elements/XSwiper/x-swiper.css"); +@import url("./src/elements/XTextarea/x-textarea.css"); +@import url("./src/elements/XList/x-list.css"); +@import url("./src/elements/XWebView/x-webview.css"); diff --git a/packages/web-platform/web-elements/package.json b/packages/web-platform/web-elements/package.json index 5b18d1eae1..e0c96281d9 100644 --- a/packages/web-platform/web-elements/package.json +++ b/packages/web-platform/web-elements/package.json @@ -18,9 +18,6 @@ "./index.css": { "default": "./index.css" }, - "./elements.css": { - "default": "./elements.css" - }, "./all": { "@lynx-js/source-field": "./src/elements/all.ts", "types": "./dist/elements/all.d.ts", @@ -115,14 +112,6 @@ "@lynx-js/source-field": "./src/elements/htmlTemplates.ts", "types": "./dist/elements/htmlTemplates.d.ts", "default": "./dist/elements/htmlTemplates.js" - }, - "./compat/LinearContainer": { - "@lynx-js/source-field": "./src/compat/LinearContainer/LinearContainer.ts", - "types": "./dist/compat/LinearContainer/LinearContainer.d.ts", - "default": "./dist/compat/LinearContainer/LinearContainer.js" - }, - "./compat/LinearContainer/linear-compat.css": { - "default": "./src/compat/LinearContainer/linear-compat.css" } }, "main": "dist/index.js", diff --git a/packages/web-platform/web-elements/src/compat/LinearContainer/LinearContainer.ts b/packages/web-platform/web-elements/src/compat/LinearContainer/LinearContainer.ts index 008fb9d121..0b48e06bbe 100644 --- a/packages/web-platform/web-elements/src/compat/LinearContainer/LinearContainer.ts +++ b/packages/web-platform/web-elements/src/compat/LinearContainer/LinearContainer.ts @@ -4,7 +4,6 @@ import { type AttributeReactiveClass, bindToAttribute, - type WebComponentClass, } from '../../element-reactive/index.js'; import '../../../src/compat/LinearContainer/linear-compat.css'; @@ -56,7 +55,7 @@ const supportContainerStyleQuery = CSS.supports('width:1rex') && CSS.supports('transition-behavior:allow-discrete') && CSS.supports('content-visibility: auto'); -export class LinearContainer +class LinearContainerImpl implements InstanceType> { static readonly observedAttributes = []; @@ -87,49 +86,6 @@ export class LinearContainer ); } -/** - * remove this once firefox supports @container style() - * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@container - * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1795622 - */ -if (!supportContainerStyleQuery) { - const targetElements = new Set([ - 'x-view', - 'scroll-view', - 'x-foldview-header-ng', - 'x-foldview-ng', - 'x-foldview-slot-drag-ng', - 'x-foldview-slot-ng', - 'x-foldview-toolbar-ng', - 'x-refresh-footer', - 'x-refresh-header', - 'x-refresh-view', - 'x-swiper-item', - 'x-viewpager-item-ng', - 'x-viewpager-ng', - ]); - const realDefine = customElements.define.bind(customElements); - const fakeDefine = ( - name: string, - cls: CustomElementConstructor, - options: any, - ) => { - if (targetElements.has(name)) { - (cls as WebComponentClass).registerPlugin?.( - LinearContainer, - ); - targetElements.delete(name); - } - realDefine(name, cls, options); - }; - customElements.define = fakeDefine; - for (const tag of targetElements) { - (customElements.whenDefined(tag)).then((cls: WebComponentClass) => { - if (targetElements.has(tag)) { - cls.registerPlugin?.( - LinearContainer, - ); - } - }); - } -} +export const LinearContainer = supportContainerStyleQuery + ? undefined + : LinearContainerImpl; diff --git a/packages/web-platform/web-elements/src/compat/LinearContainer/linear-compat.css b/packages/web-platform/web-elements/src/compat/LinearContainer/linear-compat.css index 0f11131623..fc1764f2d7 100644 --- a/packages/web-platform/web-elements/src/compat/LinearContainer/linear-compat.css +++ b/packages/web-platform/web-elements/src/compat/LinearContainer/linear-compat.css @@ -47,78 +47,78 @@ --flex-wrap: nowrap; --align-self: auto; } -} -[lynx-computed-display="linear"] { - flex-wrap: nowrap !important; - flex-direction: column; - justify-content: flex-start; -} + [lynx-computed-display="linear"] { + flex-wrap: nowrap !important; + flex-direction: column; + justify-content: flex-start; + } -[lynx-computed-display="flex"] { - flex-direction: var(--flex-direction); - justify-content: var(--justify-content); - flex-wrap: var(--flex-wrap); -} + [lynx-computed-display="flex"] { + flex-direction: var(--flex-direction); + justify-content: var(--justify-content); + flex-wrap: var(--flex-wrap); + } -[lynx-computed-display="flex"] > *, -[lynx-computed-display="flex"] > lynx-wrapper > * { - flex: var( - --flex, - var(--flex-grow) var(--flex-shrink) var(--flex-basis) - ); -} -[lynx-computed-display="linear"] > *, -[lynx-computed-display="linear"] > lynx-wrapper > * { - flex-shrink: 0 !important; - flex-grow: calc( - var(--lynx-linear-weight) / - calc( - var(--lynx-linear-weight-sum) + - ( - 1 - clamp(0, var(--lynx-linear-weight-sum) * 999999, 1) + [lynx-computed-display="flex"] > *, + [lynx-computed-display="flex"] > lynx-wrapper > * { + flex: var( + --flex, + var(--flex-grow) var(--flex-shrink) var(--flex-basis) + ); + } + [lynx-computed-display="linear"] > *, + [lynx-computed-display="linear"] > lynx-wrapper > * { + flex-shrink: 0 !important; + flex-grow: calc( + var(--lynx-linear-weight) / + calc( + var(--lynx-linear-weight-sum) + + ( + 1 - clamp(0, var(--lynx-linear-weight-sum) * 999999, 1) + ) ) - ) - ) !important; - flex-basis: var(--lynx-linear-weight-basis) !important; -} + ) !important; + flex-basis: var(--lynx-linear-weight-basis) !important; + } -[lynx-computed-display="linear"][lynx-linear-orientation="vertical"] { - flex-direction: column !important; - justify-content: var(--justify-content-column); -} + [lynx-computed-display="linear"][lynx-linear-orientation="vertical"] { + flex-direction: column !important; + justify-content: var(--justify-content-column); + } -[lynx-computed-display="linear"][lynx-linear-orientation="horizontal"] { - flex-direction: row !important; - justify-content: var(--justify-content-row); -} + [lynx-computed-display="linear"][lynx-linear-orientation="horizontal"] { + flex-direction: row !important; + justify-content: var(--justify-content-row); + } -[lynx-computed-display="linear"][lynx-linear-orientation="vertical-reverse"] { - flex-direction: column-reverse !important; - justify-content: var(--justify-content-column-reverse); -} + [lynx-computed-display="linear"][lynx-linear-orientation="vertical-reverse"] { + flex-direction: column-reverse !important; + justify-content: var(--justify-content-column-reverse); + } -[lynx-computed-display="linear"][lynx-linear-orientation="horizontal-reverse"] { - flex-direction: row-reverse !important; - justify-content: var(--justify-content-row-reverse); -} + [lynx-computed-display="linear"][lynx-linear-orientation="horizontal-reverse"] { + flex-direction: row-reverse !important; + justify-content: var(--justify-content-row-reverse); + } -[lynx-computed-display="linear"][lynx-linear-orientation="vertical"] > *, -[lynx-computed-display="linear"][lynx-linear-orientation="vertical-reverse"], -[lynx-computed-display="linear"][lynx-linear-orientation="vertical"] - > lynx-wrapper - > *, -[lynx-computed-display="linear"][lynx-linear-orientation="vertical-reverse"] - > lynx-wrapper - > * { - align-self: var(--align-self-column); -} -[lynx-computed-display="linear"][lynx-linear-orientation="horizontal"] > *, -[lynx-computed-display="linear"][lynx-linear-orientation="horizontal-reverse"], -[lynx-computed-display="linear"][lynx-linear-orientation="horizontal"] - > lynx-wrapper - > *, -[lynx-computed-display="linear"][lynx-linear-orientation="horizontal-reverse"] - > lynx-wrapper - > * { - align-self: var(--align-self-row); + [lynx-computed-display="linear"][lynx-linear-orientation="vertical"] > *, + [lynx-computed-display="linear"][lynx-linear-orientation="vertical-reverse"], + [lynx-computed-display="linear"][lynx-linear-orientation="vertical"] + > lynx-wrapper + > *, + [lynx-computed-display="linear"][lynx-linear-orientation="vertical-reverse"] + > lynx-wrapper + > * { + align-self: var(--align-self-column); + } + [lynx-computed-display="linear"][lynx-linear-orientation="horizontal"] > *, + [lynx-computed-display="linear"][lynx-linear-orientation="horizontal-reverse"], + [lynx-computed-display="linear"][lynx-linear-orientation="horizontal"] + > lynx-wrapper + > *, + [lynx-computed-display="linear"][lynx-linear-orientation="horizontal-reverse"] + > lynx-wrapper + > * { + align-self: var(--align-self-row); + } } diff --git a/packages/web-platform/web-elements/src/compat/index.ts b/packages/web-platform/web-elements/src/compat/index.ts new file mode 100644 index 0000000000..a16f84b3f8 --- /dev/null +++ b/packages/web-platform/web-elements/src/compat/index.ts @@ -0,0 +1 @@ +export { LinearContainer } from './LinearContainer/LinearContainer.js'; diff --git a/packages/web-platform/web-elements/src/element-reactive/component.ts b/packages/web-platform/web-elements/src/element-reactive/component.ts index 44f943d817..c46b16ecf0 100644 --- a/packages/web-platform/web-elements/src/element-reactive/component.ts +++ b/packages/web-platform/web-elements/src/element-reactive/component.ts @@ -64,10 +64,13 @@ export type AttributeReactiveObject = { export function Component( tag: string, - attributeReactiveClasses: AttributeReactiveClass[], + attributeReactiveClassesOptional: (AttributeReactiveClass | undefined)[], template?: string, ): (target: T, context: ClassDecoratorContext) => T { let templateElement: HTMLTemplateElement | undefined; + const attributeReactiveClasses = attributeReactiveClassesOptional.filter( + (e): e is AttributeReactiveClass => Boolean(e), + ); return (target: T, { addInitializer }): T => { const observedStyleProperties = new Set([ ...attributeReactiveClasses @@ -269,36 +272,58 @@ export function Component( } > = {}; - override addEventListener( - type: string, - listener: EventListener, - options?: AddEventListenerOptions | boolean, - ): void { - super.addEventListener(type, listener, options); - this.#eventListenerMap[type] ??= { + public enableEvent(eventName: string): void { + this.#eventListenerMap[eventName] ??= { count: 0, listenerCount: new WeakMap(), captureListenerCount: new WeakMap(), }; - const targetEventInfo = this.#eventListenerMap[type]; - const capture = typeof options === 'object' ? options.capture : options; - const targetMap = capture - ? targetEventInfo.captureListenerCount - : targetEventInfo.listenerCount; - const currentListenerCount = targetMap.get(listener) ?? 0; - targetMap.set(listener, currentListenerCount + 1); + const targetEventInfo = this.#eventListenerMap[eventName]; if (targetEventInfo.count === 0) { // trigger eventStatusChangeHandler for (const oneReactive of this.#attributeReactives) { - const handler = oneReactive.eventStatusChangedHandler?.[type]; + const handler = oneReactive.eventStatusChangedHandler?.[eventName]; if (handler) { - handler.call(oneReactive, true, type); + handler.call(oneReactive, true, eventName); } } } targetEventInfo.count++; } + public disableEvent(eventName: string): void { + const targetEventInfo = this.#eventListenerMap[eventName]; + if (targetEventInfo && targetEventInfo.count > 0) { + targetEventInfo.count--; + if (targetEventInfo.count === 0) { + // trigger eventStatusChangeHandler + for (const oneReactive of this.#attributeReactives) { + const handler = oneReactive.eventStatusChangedHandler + ?.[eventName]; + if (handler) { + handler.call(oneReactive, false, eventName); + } + } + } + } + } + + override addEventListener( + type: string, + listener: EventListener, + options?: AddEventListenerOptions | boolean, + ): void { + super.addEventListener(type, listener, options); + this.enableEvent(type); + const targetEventInfo = this.#eventListenerMap[type]!; + const capture = typeof options === 'object' ? options.capture : options; + const targetMap = capture + ? targetEventInfo.captureListenerCount + : targetEventInfo.listenerCount; + const currentListenerCount = targetMap.get(listener) ?? 0; + targetMap.set(listener, currentListenerCount + 1); + } + override removeEventListener( type: string, listener: EventListener, @@ -309,21 +334,12 @@ export function Component( const targetEventInfo = this.#eventListenerMap[type]; if (targetEventInfo && targetEventInfo.count > 0) { const targetMap = capture - ? targetEventInfo?.captureListenerCount - : targetEventInfo?.listenerCount; + ? targetEventInfo.captureListenerCount + : targetEventInfo.listenerCount; const currentListenerCount = targetMap.get(listener); if (currentListenerCount === 1) { - targetEventInfo.listenerCount.delete(listener); - targetEventInfo.count--; - if (targetEventInfo.count === 0) { - // trigger eventStatusChangeHandler - for (const oneReactive of this.#attributeReactives) { - const handler = oneReactive.eventStatusChangedHandler?.[type]; - if (handler) { - handler.call(oneReactive, false, type); - } - } - } + targetMap.delete(listener); + this.disableEvent(type); } } } diff --git a/packages/web-platform/web-elements/src/elements/ScrollView/ScrollView.ts b/packages/web-platform/web-elements/src/elements/ScrollView/ScrollView.ts index 004ade27db..946929545c 100644 --- a/packages/web-platform/web-elements/src/elements/ScrollView/ScrollView.ts +++ b/packages/web-platform/web-elements/src/elements/ScrollView/ScrollView.ts @@ -11,10 +11,12 @@ import { ScrollIntoView } from './ScrollIntoView.js'; import { Component } from '../../element-reactive/index.js'; import { scrollContainerDom } from '../common/constants.js'; import { templateScrollView } from '../htmlTemplates.js'; +import { LinearContainer } from '../../compat/index.js'; @Component( 'scroll-view', [ + LinearContainer, CommonEventsAndMethods, ScrollAttributes, FadeEdgeLengthAttribute, diff --git a/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewHeaderNg.ts b/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewHeaderNg.ts index 4fc2422338..d6cd7cac66 100644 --- a/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewHeaderNg.ts +++ b/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewHeaderNg.ts @@ -7,10 +7,12 @@ import { Component } from '../../element-reactive/index.js'; import { CommonEventsAndMethods } from '../common/CommonEventsAndMethods.js'; import { resizeObserver, type XFoldviewNg } from './XFoldviewNg.js'; import { getCombinedDirectParentElement } from '../common/getCombinedParentElement.js'; +import { LinearContainer } from '../../compat/index.js'; @Component( 'x-foldview-header-ng', [ + LinearContainer, CommonEventsAndMethods, ], ) diff --git a/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewNg.ts b/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewNg.ts index 5574b7da37..10f4482bd8 100644 --- a/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewNg.ts +++ b/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewNg.ts @@ -8,6 +8,7 @@ import { CommonEventsAndMethods } from '../common/CommonEventsAndMethods.js'; import { XFoldviewNgEvents } from './XFoldviewNgEvents.js'; import { scrollContainerDom } from '../common/constants.js'; import type { XFoldviewSlotNg } from './XFoldviewSlotNg.js'; +import { LinearContainer } from '../../compat/index.js'; export const scrollableLength = Symbol('scrollableLength'); export const isHeaderShowing = Symbol('isHeaderShowing'); @@ -15,6 +16,7 @@ export const resizeObserver = Symbol('resizeObserver'); export const slotKid = Symbol('slotKid'); @Component('x-foldview-ng', [ + LinearContainer, CommonEventsAndMethods, XFoldviewNgEvents, ]) diff --git a/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewSlotDragNg.ts b/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewSlotDragNg.ts index 24156f1cd7..427b2e6aae 100644 --- a/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewSlotDragNg.ts +++ b/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewSlotDragNg.ts @@ -5,8 +5,10 @@ */ import { Component } from '../../element-reactive/index.js'; import { CommonEventsAndMethods } from '../common/CommonEventsAndMethods.js'; +import { LinearContainer } from '../../compat/index.js'; @Component('x-foldview-slot-drag-ng', [ + LinearContainer, CommonEventsAndMethods, ]) export class XFoldviewSlotDragNg extends HTMLElement {} diff --git a/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewSlotNg.ts b/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewSlotNg.ts index 282800d8dc..24b21c3045 100644 --- a/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewSlotNg.ts +++ b/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewSlotNg.ts @@ -7,10 +7,12 @@ import { Component } from '../../element-reactive/index.js'; import { CommonEventsAndMethods } from '../common/CommonEventsAndMethods.js'; import { XFoldviewSlotNgTouchEventsHandler } from './XFoldviewSlotNgTouchEventsHandler.js'; import { slotKid, type XFoldviewNg } from './XFoldviewNg.js'; +import { LinearContainer } from '../../compat/index.js'; @Component( 'x-foldview-slot-ng', [ + LinearContainer, CommonEventsAndMethods, XFoldviewSlotNgTouchEventsHandler, ], diff --git a/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewToolbarNg.ts b/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewToolbarNg.ts index a2584d5a98..9554d7f1e2 100644 --- a/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewToolbarNg.ts +++ b/packages/web-platform/web-elements/src/elements/XFoldViewNg/XFoldviewToolbarNg.ts @@ -7,8 +7,10 @@ import { Component } from '../../element-reactive/index.js'; import { CommonEventsAndMethods } from '../common/CommonEventsAndMethods.js'; import { resizeObserver, type XFoldviewNg } from './XFoldviewNg.js'; import { getCombinedDirectParentElement } from '../common/getCombinedParentElement.js'; +import { LinearContainer } from '../../compat/index.js'; @Component('x-foldview-toolbar-ng', [ + LinearContainer, CommonEventsAndMethods, ]) export class XFoldviewToolbarNg extends HTMLElement { diff --git a/packages/web-platform/web-elements/src/elements/XRefreshView/XRefreshFooter.ts b/packages/web-platform/web-elements/src/elements/XRefreshView/XRefreshFooter.ts index 6dc52822f2..c7034a1d97 100644 --- a/packages/web-platform/web-elements/src/elements/XRefreshView/XRefreshFooter.ts +++ b/packages/web-platform/web-elements/src/elements/XRefreshView/XRefreshFooter.ts @@ -6,10 +6,12 @@ import { Component } from '../../element-reactive/index.js'; import { XRefreshSubElementIntersectionObserver } from './XRefreshSubElementIntersectionObserver.js'; import { CommonEventsAndMethods } from '../common/CommonEventsAndMethods.js'; +import { LinearContainer } from '../../compat/index.js'; @Component( 'x-refresh-footer', [ + LinearContainer, CommonEventsAndMethods, XRefreshSubElementIntersectionObserver, ], diff --git a/packages/web-platform/web-elements/src/elements/XRefreshView/XRefreshHeader.ts b/packages/web-platform/web-elements/src/elements/XRefreshView/XRefreshHeader.ts index 378e38eaf5..1f0ce7a1ae 100644 --- a/packages/web-platform/web-elements/src/elements/XRefreshView/XRefreshHeader.ts +++ b/packages/web-platform/web-elements/src/elements/XRefreshView/XRefreshHeader.ts @@ -6,10 +6,12 @@ import { Component } from '../../element-reactive/index.js'; import { XRefreshSubElementIntersectionObserver } from './XRefreshSubElementIntersectionObserver.js'; import { CommonEventsAndMethods } from '../common/CommonEventsAndMethods.js'; +import { LinearContainer } from '../../compat/index.js'; @Component( 'x-refresh-header', [ + LinearContainer, CommonEventsAndMethods, XRefreshSubElementIntersectionObserver, ], diff --git a/packages/web-platform/web-elements/src/elements/XRefreshView/XRefreshView.ts b/packages/web-platform/web-elements/src/elements/XRefreshView/XRefreshView.ts index f7e2a43fd1..52b5b65aac 100644 --- a/packages/web-platform/web-elements/src/elements/XRefreshView/XRefreshView.ts +++ b/packages/web-platform/web-elements/src/elements/XRefreshView/XRefreshView.ts @@ -8,10 +8,11 @@ import { XRefreshViewEventsEmitter } from './XRefreshViewEventsEmitter.js'; import { CommonEventsAndMethods } from '../common/CommonEventsAndMethods.js'; import { scrollContainerDom } from '../common/constants.js'; import { templateXRefreshView } from '../htmlTemplates.js'; +import { LinearContainer } from '../../compat/index.js'; @Component( 'x-refresh-view', - [CommonEventsAndMethods, XRefreshViewEventsEmitter], + [LinearContainer, CommonEventsAndMethods, XRefreshViewEventsEmitter], templateXRefreshView, ) export class XRefreshView extends HTMLElement { diff --git a/packages/web-platform/web-elements/src/elements/XSwiper/SwiperItem.ts b/packages/web-platform/web-elements/src/elements/XSwiper/SwiperItem.ts index 14b8570254..d809fe025e 100644 --- a/packages/web-platform/web-elements/src/elements/XSwiper/SwiperItem.ts +++ b/packages/web-platform/web-elements/src/elements/XSwiper/SwiperItem.ts @@ -5,6 +5,10 @@ */ import { Component } from '../../element-reactive/index.js'; import { CommonEventsAndMethods } from '../common/CommonEventsAndMethods.js'; +import { LinearContainer } from '../../compat/index.js'; -@Component('x-swiper-item', [CommonEventsAndMethods]) +@Component('x-swiper-item', [ + LinearContainer, + CommonEventsAndMethods, +]) export class SwiperItem extends HTMLElement {} diff --git a/packages/web-platform/web-elements/src/elements/XView/XView.ts b/packages/web-platform/web-elements/src/elements/XView/XView.ts index cc355c4d84..384eabea10 100644 --- a/packages/web-platform/web-elements/src/elements/XView/XView.ts +++ b/packages/web-platform/web-elements/src/elements/XView/XView.ts @@ -9,8 +9,10 @@ import { CommonEventsAndMethods, layoutChangeTarget, } from '../common/CommonEventsAndMethods.js'; +import { LinearContainer } from '../../compat/index.js'; @Component('x-view', [ + LinearContainer, CommonEventsAndMethods, ]) export class XView extends HTMLElement { diff --git a/packages/web-platform/web-elements/src/elements/XViewpagerNg/XViewpagerItemNg.ts b/packages/web-platform/web-elements/src/elements/XViewpagerNg/XViewpagerItemNg.ts index a44f04d20e..aaf51a64cf 100644 --- a/packages/web-platform/web-elements/src/elements/XViewpagerNg/XViewpagerItemNg.ts +++ b/packages/web-platform/web-elements/src/elements/XViewpagerNg/XViewpagerItemNg.ts @@ -5,8 +5,10 @@ */ import { Component } from '../../element-reactive/index.js'; import { CommonEventsAndMethods } from '../common/CommonEventsAndMethods.js'; +import { LinearContainer } from '../../compat/index.js'; @Component('x-viewpager-item-ng', [ + LinearContainer, CommonEventsAndMethods, ]) export class XViewpagerItemNg extends HTMLElement {} diff --git a/packages/web-platform/web-elements/src/elements/XViewpagerNg/XViewpagerNg.ts b/packages/web-platform/web-elements/src/elements/XViewpagerNg/XViewpagerNg.ts index 21c893d761..2382f8eb9b 100644 --- a/packages/web-platform/web-elements/src/elements/XViewpagerNg/XViewpagerNg.ts +++ b/packages/web-platform/web-elements/src/elements/XViewpagerNg/XViewpagerNg.ts @@ -11,10 +11,11 @@ import { CommonEventsAndMethods } from '../common/CommonEventsAndMethods.js'; import { XViewpagerNgEvents } from './XViewpagerNgEvents.js'; import { scrollContainerDom } from '../common/constants.js'; import { templateXViewpageNg } from '../htmlTemplates.js'; +import { LinearContainer } from '../../compat/index.js'; @Component( 'x-viewpager-ng', - [CommonEventsAndMethods, XViewpagerNgEvents], + [LinearContainer, CommonEventsAndMethods, XViewpagerNgEvents], templateXViewpageNg, ) export class XViewpagerNg extends HTMLElement { diff --git a/packages/web-platform/web-elements/src/elements/common-css/linear.css b/packages/web-platform/web-elements/src/elements/common-css/linear.css index 1298503222..5330aea215 100644 --- a/packages/web-platform/web-elements/src/elements/common-css/linear.css +++ b/packages/web-platform/web-elements/src/elements/common-css/linear.css @@ -258,7 +258,6 @@ list-item { * * update this once firefox supports this. * - * If you want to be fully compatible with chrome below 117, you need to use @lynx-js/web-elements/compat/LinearContainer. */ @supports (content-visibility: auto) and (transition-behavior: allow-discrete) and (width: 1rex) { diff --git a/packages/web-platform/web-elements/tests/component-event.spec.ts b/packages/web-platform/web-elements/tests/component-event.spec.ts new file mode 100644 index 0000000000..d9c4f5a587 --- /dev/null +++ b/packages/web-platform/web-elements/tests/component-event.spec.ts @@ -0,0 +1,125 @@ +import { test, expect } from '@lynx-js/playwright-fixtures'; + +const gotoWebComponentPage = async (page: any, testname: string) => { + await page.goto(`/tests/fixtures/${testname}.html`, { + waitUntil: 'load', + }); + await page.evaluate(() => document.fonts.ready); +}; + +test.describe('Component Event Logic', () => { + test('enableEvent and disableEvent should trigger status changes correctly', async ({ page }) => { + await gotoWebComponentPage(page, 'component-event-test'); + + // Check initial state + const initialEvents = await page.evaluate(() => + (window as any).eventEvents + ); + expect(initialEvents).toBeUndefined(); + + // Wait for custom element to be defined + await page.evaluate(() => customElements.whenDefined('x-event-test')); + + // Check availability + const isDefined = await page.evaluate(() => + !!customElements.get('x-event-test') + ); + expect(isDefined).toBe(true); + + // Call enableEvent + await page.evaluate(() => { + const el = document.querySelector('x-event-test') as any; + if (!el) throw new Error('x-event-test not found in DOM'); + el.enableEvent('custom-event'); + }); + + // Check events: should be enabled + let events = await page.evaluate(() => (window as any).eventEvents); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ type: 'custom-event', status: true }); + + // Call enableEvent again + await page.evaluate(() => { + const el = document.querySelector('x-event-test') as any; + el.enableEvent('custom-event'); + }); + + // Check events: should not have changed + events = await page.evaluate(() => (window as any).eventEvents); + expect(events).toHaveLength(1); + + // Call disableEvent (count was 2, now 1) + await page.evaluate(() => { + const el = document.querySelector('x-event-test') as any; + el.disableEvent('custom-event'); + }); + + // Check events: should stay enabled + events = await page.evaluate(() => (window as any).eventEvents); + expect(events).toHaveLength(1); + + // Call disableEvent again (count was 1, now 0) + await page.evaluate(() => { + const el = document.querySelector('x-event-test') as any; + el.disableEvent('custom-event'); + }); + + // Check events: should be disabled + events = await page.evaluate(() => (window as any).eventEvents); + expect(events).toHaveLength(2); + expect(events[1]).toEqual({ type: 'custom-event', status: false }); + }); + + test('addEventListener and removeEventListener should trigger status changes', async ({ page }) => { + await gotoWebComponentPage(page, 'component-event-test'); + + // Reset events + await page.evaluate(() => ((window as any).eventEvents = [])); + + // addEventListener + await page.evaluate(() => { + const el = document.querySelector('x-event-test') as any; + const handler = () => {}; + (window as any).__handler = handler; + el.addEventListener('custom-event', handler); + }); + + // Check enabled + let events = await page.evaluate(() => (window as any).eventEvents); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ type: 'custom-event', status: true }); + + // addEventListener (same event, different handler) + await page.evaluate(() => { + const el = document.querySelector('x-event-test') as any; + const handler2 = () => {}; + (window as any).__handler2 = handler2; + el.addEventListener('custom-event', handler2); + }); + + // Check no change + events = await page.evaluate(() => (window as any).eventEvents); + expect(events).toHaveLength(1); + + // removeEventListener (first handler) + await page.evaluate(() => { + const el = document.querySelector('x-event-test') as any; + el.removeEventListener('custom-event', (window as any).__handler); + }); + + // Check no change + events = await page.evaluate(() => (window as any).eventEvents); + expect(events).toHaveLength(1); + + // removeEventListener (second handler) + await page.evaluate(() => { + const el = document.querySelector('x-event-test') as any; + el.removeEventListener('custom-event', (window as any).__handler2); + }); + + // Check disabled + events = await page.evaluate(() => (window as any).eventEvents); + expect(events).toHaveLength(2); + expect(events[1]).toEqual({ type: 'custom-event', status: false }); + }); +}); diff --git a/packages/web-platform/web-elements/tests/fixtures/component-event-test.html b/packages/web-platform/web-elements/tests/fixtures/component-event-test.html new file mode 100644 index 0000000000..5b6cc80ef6 --- /dev/null +++ b/packages/web-platform/web-elements/tests/fixtures/component-event-test.html @@ -0,0 +1,98 @@ + + + + + component-event-test + + + + + + + + diff --git a/packages/web-platform/web-elements/tests/fixtures/shell-project.ts b/packages/web-platform/web-elements/tests/fixtures/shell-project.ts index 89ab3a1068..a2384b3dfe 100644 --- a/packages/web-platform/web-elements/tests/fixtures/shell-project.ts +++ b/packages/web-platform/web-elements/tests/fixtures/shell-project.ts @@ -2,4 +2,10 @@ import '../../src/compat/LinearContainer/LinearContainer.js'; import '../../src/elements/all.js'; import '../../index.css'; import '@lynx-js/playwright-fixtures/common.css'; +import { Component } from '../../src/element-reactive/component.js'; +import { registerEventEnableStatusChangeHandler } from '../../src/element-reactive/registerEventStatusChangedHandler.js'; + +(globalThis as any).Component = Component; +(globalThis as any).registerEventEnableStatusChangeHandler = + registerEventEnableStatusChangeHandler; // Trigger rebuild diff --git a/packages/web-platform/web-tests/shell-project/lynx-view.ts b/packages/web-platform/web-tests/shell-project/lynx-view.ts index 6438d974ab..1238a0a30c 100644 --- a/packages/web-platform/web-tests/shell-project/lynx-view.ts +++ b/packages/web-platform/web-tests/shell-project/lynx-view.ts @@ -2,7 +2,6 @@ // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. import type { LynxView } from '@lynx-js/web-core'; -import '@lynx-js/web-elements/compat/LinearContainer'; import '@lynx-js/web-core'; import '@lynx-js/web-elements/all'; import '@lynx-js/web-elements/index.css'; diff --git a/packages/web-platform/web-tests/shell-project/web-core.ts b/packages/web-platform/web-tests/shell-project/web-core.ts index 56ceae0fe5..0e81fc3ba1 100644 --- a/packages/web-platform/web-tests/shell-project/web-core.ts +++ b/packages/web-platform/web-tests/shell-project/web-core.ts @@ -4,7 +4,6 @@ import type { LynxView } from '@lynx-js/web-core'; import '@lynx-js/web-core'; import '@lynx-js/web-elements/index.css'; -import '@lynx-js/web-elements/compat/LinearContainer'; import '@lynx-js/web-core/index.css'; import './index.css';