Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ import { useProperties, useCanvas, useHistory, useHelp } from '@opentiny/tiny-en
import { LinkButton } from '@opentiny/tiny-engine-common'
import { CodeConfigurator } from '@opentiny/tiny-engine-configurator'
import useStyle, { updateGlobalStyleStr } from '../../js/useStyle'
import { stringify, getSelectorArr, parser } from '../../js/parser'
import { stringify, getSelectorArr, buildCssObjectFromContent } from '../../js/parser'

const { getSchema, propsUpdateKey, setProp } = useProperties()

Expand Down Expand Up @@ -448,11 +448,8 @@ watchEffect(() => {
})

const save = ({ content }) => {
const { styleObject } = parser(content)
const cssObject = {}
Object.keys(styleObject).forEach((styleKey) => {
cssObject[styleKey] = styleObject[styleKey].rules
})
const cssObject = buildCssObjectFromContent(content)

const { addHistory } = useHistory()
const { updateRect } = useCanvas().canvasApi.value
const { updateSchema } = useCanvas()
Expand Down
42 changes: 42 additions & 0 deletions packages/settings/styles/src/js/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const handleAtRules = (node: any) => {

return {
type,
hasBlock: node.nodes !== undefined,
style: {
type,
value: rawString
Expand Down Expand Up @@ -143,6 +144,47 @@ export const parser = (css: string) => {
}
}

/**
* 根据编辑器 css 内容生成对象,保留源码顺序并支持 at-rule
* @param {string} content
* @returns {Record<string, any>}
*/
export const buildCssObjectFromContent = (content: string) => {
const { parseList, styleObject } = parser(content)
const cssObject = {}

// 保证存入 cssObject 的键值顺序与编辑器中的源码字符顺序一致
parseList.forEach((item) => {
// parser 中的 handleRules 没有给普通 rule 赋 type 属性,只具备 selectors 和 style
if (!item.type && item.selectors) {
if (styleObject[item.selectors]) {
cssObject[item.selectors] = styleObject[item.selectors].rules
}
} else if (item.type === 'atrule') {
const rawValue = item.style?.value || ''
let key = ''
let value = ''

if (item.hasBlock) {
const braceIdx = rawValue.indexOf('{')
key = braceIdx !== -1 ? rawValue.slice(0, braceIdx).trim() : rawValue.trim()
value = braceIdx !== -1 ? rawValue.slice(braceIdx).trim() : ''
} else {
key = rawValue.trim()
value = ''
}

if (cssObject[key] !== undefined) {
cssObject[key] = Array.isArray(cssObject[key]) ? [...cssObject[key], value] : [cssObject[key], value]
} else {
cssObject[key] = value
}
}
})

return cssObject
}

/**
* 拿到组合选择器的数组,比如 .test1.test2 得到 ['.test1', '.test2']
* @param {string} selector
Expand Down
20 changes: 19 additions & 1 deletion packages/utils/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,9 +464,27 @@ export const objectCssToString = (css) => {
return css
}
let cssString = ''
let topString = ''

for (const selector in css) {
const properties = css[selector]

if (typeof properties === 'string' || (Array.isArray(properties) && typeof properties[0] === 'string')) {
const isTopRule =
selector.startsWith('@charset') || selector.startsWith('@import') || selector.startsWith('@namespace')
const propsArray = Array.isArray(properties) ? properties : [properties]

propsArray.forEach((prop) => {
const line = prop ? `${selector} ${prop}\n` : `${selector}\n`
if (isTopRule) {
topString += line
} else {
cssString += line
}
})
continue
}

let ruleString = `${selector} {\r\n`

for (const property in properties) {
Expand All @@ -476,5 +494,5 @@ export const objectCssToString = (css) => {
ruleString += '}\n'
cssString += ruleString
}
return cssString
return topString + cssString
}