mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(skin): improved responsive design (#1832)
This commit is contained in:
@@ -17,42 +17,62 @@ export function resolveCSSLength(el: Element, value: string): number {
|
||||
|
||||
const parsed = Number.parseFloat(trimmed);
|
||||
|
||||
if (Number.isNaN(parsed)) return 0;
|
||||
if (/^-?\d*\.?\d+$/.test(trimmed) || trimmed.endsWith('px')) return parsed;
|
||||
if (!Number.isNaN(parsed) && (/^-?\d*\.?\d+$/.test(trimmed) || trimmed.endsWith('px'))) return parsed;
|
||||
|
||||
const doc = el.ownerDocument;
|
||||
const root = doc?.documentElement;
|
||||
|
||||
if (trimmed.endsWith('rem')) {
|
||||
if (!Number.isNaN(parsed) && trimmed.endsWith('rem')) {
|
||||
const rootFontSize = root ? Number.parseFloat(getComputedStyle(root).fontSize) || 16 : 16;
|
||||
return parsed * rootFontSize;
|
||||
}
|
||||
|
||||
if (trimmed.endsWith('em')) {
|
||||
if (!Number.isNaN(parsed) && trimmed.endsWith('em')) {
|
||||
const fontSize = el instanceof HTMLElement ? Number.parseFloat(getComputedStyle(el).fontSize) || 16 : 16;
|
||||
return parsed * fontSize;
|
||||
}
|
||||
|
||||
if (!doc) return parsed;
|
||||
if (!doc) return Number.isNaN(parsed) ? 0 : parsed;
|
||||
|
||||
const measurementEl = doc.createElement('div');
|
||||
measurementEl.style.position = 'absolute';
|
||||
measurementEl.style.visibility = 'hidden';
|
||||
measurementEl.style.pointerEvents = 'none';
|
||||
measurementEl.style.inlineSize = trimmed;
|
||||
|
||||
if (!measurementEl.style.inlineSize) return 0;
|
||||
|
||||
measurementEl.style.blockSize = '0';
|
||||
measurementEl.style.padding = '0';
|
||||
measurementEl.style.border = '0';
|
||||
measurementEl.style.inset = '0';
|
||||
|
||||
const computed = getComputedStyle(el);
|
||||
measurementEl.style.fontSize = computed.fontSize;
|
||||
|
||||
for (let i = 0; i < computed.length; i++) {
|
||||
const name = computed.item(i);
|
||||
|
||||
if (name.startsWith('--')) {
|
||||
measurementEl.style.setProperty(name, computed.getPropertyValue(name));
|
||||
}
|
||||
}
|
||||
|
||||
const parent = doc.body ?? doc.documentElement;
|
||||
|
||||
if (!parent) return parsed;
|
||||
if (!parent) return Number.isNaN(parsed) ? 0 : parsed;
|
||||
|
||||
parent.appendChild(measurementEl);
|
||||
|
||||
if (getComputedStyle(measurementEl).inlineSize === 'auto') {
|
||||
measurementEl.remove();
|
||||
return 0;
|
||||
}
|
||||
|
||||
const pixels = measurementEl.getBoundingClientRect().width;
|
||||
measurementEl.remove();
|
||||
|
||||
return Number.isFinite(pixels) ? pixels : parsed;
|
||||
if (Number.isFinite(pixels)) return pixels;
|
||||
|
||||
return Number.isNaN(parsed) ? 0 : parsed;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { resolveCSSLength } from '../style';
|
||||
|
||||
describe('resolveCSSLength', () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('Returns px values directly', () => {
|
||||
const el = document.createElement('div');
|
||||
|
||||
@@ -98,4 +102,139 @@ describe('resolveCSSLength', () => {
|
||||
|
||||
createElementSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('Returns zero for invalid CSS lengths', () => {
|
||||
const el = document.createElement('div');
|
||||
const createElement = document.createElement.bind(document);
|
||||
const createElementSpy = vi.spyOn(document, 'createElement').mockImplementation((tagName: string) => {
|
||||
const node = createElement(tagName);
|
||||
|
||||
if (tagName === 'div') {
|
||||
vi.spyOn(node, 'getBoundingClientRect').mockImplementation(() => ({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 640,
|
||||
height: 0,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 640,
|
||||
bottom: 0,
|
||||
toJSON() {},
|
||||
}));
|
||||
}
|
||||
|
||||
return node;
|
||||
});
|
||||
|
||||
expect(resolveCSSLength(el, 'not-a-length')).toBe(0);
|
||||
|
||||
createElementSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('Returns zero when a CSS length resolves to auto', () => {
|
||||
const el = document.createElement('div');
|
||||
const createElement = document.createElement.bind(document);
|
||||
const getComputedStyleSpy = vi
|
||||
.spyOn(globalThis, 'getComputedStyle')
|
||||
.mockImplementation((target: Element) =>
|
||||
target === el
|
||||
? ({ length: 0, fontSize: '14px' } as CSSStyleDeclaration)
|
||||
: ({ inlineSize: 'auto' } as CSSStyleDeclaration)
|
||||
);
|
||||
const createElementSpy = vi.spyOn(document, 'createElement').mockImplementation((tagName: string) => {
|
||||
const node = createElement(tagName);
|
||||
|
||||
if (tagName === 'div') {
|
||||
vi.spyOn(node, 'getBoundingClientRect').mockImplementation(() => ({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 640,
|
||||
height: 0,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 640,
|
||||
bottom: 0,
|
||||
toJSON() {},
|
||||
}));
|
||||
}
|
||||
|
||||
return node;
|
||||
});
|
||||
|
||||
expect(resolveCSSLength(el, 'var(--missing-size)')).toBe(0);
|
||||
|
||||
createElementSpy.mockRestore();
|
||||
getComputedStyleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('Falls back to measurement for calc values', () => {
|
||||
const el = document.createElement('div');
|
||||
const createElement = document.createElement.bind(document);
|
||||
const createElementSpy = vi.spyOn(document, 'createElement').mockImplementation((tagName: string) => {
|
||||
const node = createElement(tagName);
|
||||
|
||||
if (tagName === 'div') {
|
||||
vi.spyOn(node, 'getBoundingClientRect').mockImplementation(() => ({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: node.style.inlineSize.startsWith('calc(') ? 8 : 0,
|
||||
height: 0,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 8,
|
||||
bottom: 0,
|
||||
toJSON() {},
|
||||
}));
|
||||
}
|
||||
|
||||
return node;
|
||||
});
|
||||
|
||||
expect(resolveCSSLength(el, 'calc(0.5 * 16px)')).toBe(8);
|
||||
|
||||
createElementSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('Copies custom properties from the source element when measuring', () => {
|
||||
const el = document.createElement('div');
|
||||
|
||||
const createElement = document.createElement.bind(document);
|
||||
const getComputedStyleSpy = vi.spyOn(globalThis, 'getComputedStyle').mockImplementation(
|
||||
() =>
|
||||
({
|
||||
length: 1,
|
||||
fontSize: '14px',
|
||||
item(index: number) {
|
||||
return index === 0 ? '--size' : '';
|
||||
},
|
||||
getPropertyValue(name: string) {
|
||||
return name === '--size' ? '16px' : '';
|
||||
},
|
||||
}) as CSSStyleDeclaration
|
||||
);
|
||||
const createElementSpy = vi.spyOn(document, 'createElement').mockImplementation((tagName: string) => {
|
||||
const node = createElement(tagName);
|
||||
|
||||
if (tagName === 'div') {
|
||||
vi.spyOn(node, 'getBoundingClientRect').mockImplementation(() => ({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: node.style.getPropertyValue('--size') === '16px' ? 8 : 0,
|
||||
height: 0,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 8,
|
||||
bottom: 0,
|
||||
toJSON() {},
|
||||
}));
|
||||
}
|
||||
|
||||
return node;
|
||||
});
|
||||
|
||||
expect(resolveCSSLength(el, 'calc(0.5 * var(--size))')).toBe(8);
|
||||
|
||||
createElementSpy.mockRestore();
|
||||
getComputedStyleSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user