Skip to content

Commit

Permalink
Use css nesting
Browse files Browse the repository at this point in the history
  • Loading branch information
mantou132 committed Apr 4, 2024
1 parent 3b005a4 commit e8f7b33
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 55 deletions.
9 changes: 0 additions & 9 deletions packages/gem/docs/en/004-blog/006-es-decorators.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,3 @@ let MyElement = (() => {

- For reactive Attributes, element updates cannot be triggered when modified in DevTools, because the native `observedAttributes` cannot take effect for dynamically added attributes.
- When executing the initialization function of `@attribute`, some hacking work is required, and the performance will be slightly reduced
- The element must be inserted into the document for the properties to be read correctly - only the document is inserted before there is any [chance](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Public_class_fields#description) of removing the fields on the element
> [!CAUTION]
>
> ```js
> const myEle = new MyElement();
> console.assert(myEle.src, undefined);
> myEle.connectedCallback();
> console.assert(myEle.src, '');
> ```
9 changes: 0 additions & 9 deletions packages/gem/docs/zh/004-blog/006-es-decorators.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,3 @@ let MyElement = (() => {

- 对于反应性的 Attribute 在 DevTools 中修改时不能触发元素更新,因为原生的 `observedAttributes` 不能为动态添加的属性生效
- 在执行 `@attribute` 的初始化函数时,需要进行一下 hack 工作,性能将会小幅度降低
- 元素必须插入文档才能正确读取属性——只有插入文档才有[机会](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Public_class_fields#description)删除元素上的字段
> [!CAUTION]
>
> ```js
> const myEle = new MyElement();
> console.assert(myEle.src, undefined);
> myEle.connectedCallback();
> console.assert(myEle.src, '');
> ```
53 changes: 20 additions & 33 deletions packages/gem/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,21 +257,29 @@ export interface StyledKeyValuePair {

/**
*
* 创建 style sheet 用于 `adoptedStyleSheets`,不支持样式更新
* @param rules string | Record<string, string> 不能动态更新的 css
* @param mediaQuery string 媒体查询 StyleSheet.media.mediaText
* 创建 style sheet 用于 `adoptedStyleSheets`,不支持样式更新,只支持自定义 CSS 属性
* @param rules string | Record<string, string>
* @param media string 媒体查询
*/
export function createCSSSheet<T extends StyledKeyValuePair>(rules: T | string, mediaQuery = ''): Sheet<T> {
const styleSheet = new CSSStyleSheet();
export function createCSSSheet<T extends StyledKeyValuePair>(rules: T | string, media?: string): Sheet<T> {
const styleSheet = new CSSStyleSheet({ media });
const sheet: any = {};
styleSheet.media.mediaText = mediaQuery;
let style = '';
if (typeof rules === 'string') {
style = rules;
} else {
Object.keys(rules).forEach((key: keyof T) => {
sheet[key] = `${key as string}-${randomStr()}`;
style += rules[key].styledContent.replace(/&/g, sheet[key]);
switch (rules[key].type) {
case 'class':
style += `.${sheet[key]} {${rules[key].styledContent}}`;
break;
case 'id':
style += `#${sheet[key]} {${rules[key].styledContent}}`;
break;
default:
style += `@keyframes ${key as string} {${rules[key].styledContent}}`;
}
});
}
styleSheet.replaceSync(style);
Expand All @@ -285,24 +293,7 @@ export function randomStr(len = 5): string {
return str;
}

const nestingRuleRegExp = new RegExp('&.*{((.|\n)*)}', 'g');
// 只支持一层嵌套
// https://drafts.csswg.org/css-nesting-1/
// https://bugzilla.mozilla.org/show_bug.cgi?id=1648037
// https://bugs.webkit.org/show_bug.cgi?id=223497
function flatStyled(style: string, type: StyledType): StyledValueObject {
const nestingRules: string[] = [];
let styledContent =
`& {${style.replace(nestingRuleRegExp, (substr) => {
nestingRules.push(substr);
return '';
})}}` + nestingRules.join('');
styledContent = styledContent.replace(/&/g, type === 'class' ? '.&' : '#&');
return { styledContent, type };
}

// 写 css 文本,在 CSSStyleSheet 中使用,使用 styed-components 高亮
// 暂时不支持 `at` 规则
//
// createCSSSheet({
// red: styled.class`
Expand All @@ -313,18 +304,14 @@ function flatStyled(style: string, type: StyledType): StyledValueObject {
// `,
// });
export const styled = {
class: (arr: TemplateStringsArray, ...args: any[]) => {
const style = raw(arr, ...args);
return flatStyled(style, 'class');
class: (arr: TemplateStringsArray, ...args: any[]): StyledValueObject => {
return { styledContent: raw(arr, ...args), type: 'class' };
},
id: (arr: TemplateStringsArray, ...args: any[]) => {
const style = raw(arr, ...args);
return flatStyled(style, 'id');
id: (arr: TemplateStringsArray, ...args: any[]): StyledValueObject => {
return { styledContent: raw(arr, ...args), type: 'id' };
},
keyframes: (arr: TemplateStringsArray, ...args: any[]): StyledValueObject => {
const keyframesContent = raw(arr, ...args);
const styledContent = `@keyframes & {${keyframesContent}}`;
return { styledContent, type: 'keyframes' };
return { styledContent: raw(arr, ...args), type: 'keyframes' };
},
};

Expand Down
16 changes: 12 additions & 4 deletions packages/gem/src/test/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {

declare global {
interface CSSRuleList {
// only test style rule
item(index: number): CSSStyleRule;
}
}
Expand Down Expand Up @@ -106,15 +107,22 @@ describe('utils 测试', () => {
`,
wrap: styled.id`
position: fixed;
animation: 3s infinite alternate slideIn;
`,
slideIn: styled.keyframes`
from {
transform: translateX(0%);
}
`,
});
expect(cssSheet.scroll.startsWith('scroll')).to.true;
const rules = cssSheet[SheetToken].cssRules;
expect(rules.item(0).selectorText.startsWith('.scroll')).to.true;
expect(rules.item(0).style.background.startsWith('red')).to.true;
expect(/\.scroll(-|\w)+:hover \*/.test(rules.item(1).selectorText)).to.true;
expect(rules.item(1).style.background.startsWith('blue')).to.true;
expect(rules.item(2).selectorText.startsWith('#wrap')).to.true;
expect(rules.item(0).style.background).to.equal('red');
expect(rules.item(0).cssRules.item(0).selectorText).to.equal('&:hover *');
expect(rules.item(0).cssRules.item(0).style.background).to.equal('blue');
expect(rules.item(1).selectorText.startsWith('#wrap')).to.true;
expect((rules.item(2) as any).name).to.equal('slideIn');
});
it('styleMap/classMap/exportPartsMap', () => {
expect(styleMap({ '--x': '1px', fontSize: '14px', content: `'*'` })).to.equal(
Expand Down

0 comments on commit e8f7b33

Please sign in to comment.