glass-easel-devtools-agent 0.9.0 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend.d.ts +50 -1
- package/dist/index.js +1 -1
- package/dist/protocol/css.d.ts +29 -17
- package/dist/protocol/dom.d.ts +30 -2
- package/package.json +6 -6
- package/src/backend.ts +335 -31
- package/src/mount_point.ts +151 -16
- package/src/overlay.ts +34 -0
- package/src/protocol/css.ts +27 -21
- package/src/protocol/dom.ts +24 -3
package/dist/protocol/css.d.ts
CHANGED
|
@@ -20,7 +20,6 @@ export type AgentRequestKind = {
|
|
|
20
20
|
removeGlassEaselStyleSheetProperty: RemoveGlassEaselStyleSheetProperty;
|
|
21
21
|
replaceGlassEaselStyleSheetProperty: ReplaceGlassEaselStyleSheetProperty;
|
|
22
22
|
replaceGlassEaselStyleSheetAllProperties: ReplaceGlassEaselStyleSheetAllProperties;
|
|
23
|
-
replaceGlassEaselStyleSheetInlineStyle: ReplaceGlassEaselStyleSheetInlineStyle;
|
|
24
23
|
};
|
|
25
24
|
export type StyleSheetId = string;
|
|
26
25
|
export type CSSNameValue = {
|
|
@@ -43,7 +42,7 @@ export type CSSStyle = {
|
|
|
43
42
|
cssText?: string;
|
|
44
43
|
};
|
|
45
44
|
export type CSSRule = {
|
|
46
|
-
styleSheetId
|
|
45
|
+
styleSheetId: StyleSheetId;
|
|
47
46
|
selectorList: {
|
|
48
47
|
selectors: {
|
|
49
48
|
text: string;
|
|
@@ -56,6 +55,7 @@ export type CSSRule = {
|
|
|
56
55
|
text: string;
|
|
57
56
|
}[];
|
|
58
57
|
inactive?: boolean;
|
|
58
|
+
ruleIndex: number;
|
|
59
59
|
};
|
|
60
60
|
export type CSSMatchedRule = {
|
|
61
61
|
rule: CSSRule;
|
|
@@ -116,6 +116,7 @@ export interface GetMatchedStylesForNode extends RequestResponse {
|
|
|
116
116
|
*/
|
|
117
117
|
export interface AddGlassEaselStyleSheetRule extends RequestResponse {
|
|
118
118
|
request: {
|
|
119
|
+
nodeId: NodeId;
|
|
119
120
|
mediaQueryText: string;
|
|
120
121
|
selector: string;
|
|
121
122
|
};
|
|
@@ -126,15 +127,19 @@ export interface AddGlassEaselStyleSheetRule extends RequestResponse {
|
|
|
126
127
|
export interface GetGlassEaselStyleSheetIndexForNewRules extends RequestResponse {
|
|
127
128
|
request: Record<string, never>;
|
|
128
129
|
response: {
|
|
130
|
+
nodeId: NodeId;
|
|
129
131
|
styleSheetId: StyleSheetId;
|
|
130
132
|
};
|
|
131
133
|
}
|
|
132
134
|
/**
|
|
133
135
|
* Clear a CSS rule.
|
|
136
|
+
*
|
|
137
|
+
* `styleSheetId = undefined` refers to the inline styles.
|
|
134
138
|
*/
|
|
135
139
|
export interface ResetGlassEaselStyleSheetRule extends RequestResponse {
|
|
136
140
|
request: {
|
|
137
|
-
|
|
141
|
+
nodeId: NodeId;
|
|
142
|
+
styleSheetId?: StyleSheetId;
|
|
138
143
|
ruleIndex: number;
|
|
139
144
|
};
|
|
140
145
|
}
|
|
@@ -143,6 +148,7 @@ export interface ResetGlassEaselStyleSheetRule extends RequestResponse {
|
|
|
143
148
|
*/
|
|
144
149
|
export interface ModifyGlassEaselStyleSheetRuleSelector extends RequestResponse {
|
|
145
150
|
request: {
|
|
151
|
+
nodeId: NodeId;
|
|
146
152
|
styleSheetId: StyleSheetId;
|
|
147
153
|
ruleIndex: number;
|
|
148
154
|
selector: string;
|
|
@@ -150,20 +156,26 @@ export interface ModifyGlassEaselStyleSheetRuleSelector extends RequestResponse
|
|
|
150
156
|
}
|
|
151
157
|
/**
|
|
152
158
|
* Add a new CSS property.
|
|
159
|
+
*
|
|
160
|
+
* `styleSheetId = undefined` refers to the inline styles.
|
|
153
161
|
*/
|
|
154
162
|
export interface AddGlassEaselStyleSheetProperty extends RequestResponse {
|
|
155
163
|
request: {
|
|
156
|
-
|
|
164
|
+
nodeId: NodeId;
|
|
165
|
+
styleSheetId?: StyleSheetId;
|
|
157
166
|
ruleIndex: number;
|
|
158
167
|
styleText: string;
|
|
159
168
|
};
|
|
160
169
|
}
|
|
161
170
|
/**
|
|
162
171
|
* Set the disabled status of a new CSS property.
|
|
172
|
+
*
|
|
173
|
+
* `styleSheetId = undefined` refers to the inline styles.
|
|
163
174
|
*/
|
|
164
175
|
export interface SetGlassEaselStyleSheetPropertyDisabled extends RequestResponse {
|
|
165
176
|
request: {
|
|
166
|
-
|
|
177
|
+
nodeId: NodeId;
|
|
178
|
+
styleSheetId?: StyleSheetId;
|
|
167
179
|
ruleIndex: number;
|
|
168
180
|
propertyIndex: number;
|
|
169
181
|
disabled: boolean;
|
|
@@ -171,20 +183,26 @@ export interface SetGlassEaselStyleSheetPropertyDisabled extends RequestResponse
|
|
|
171
183
|
}
|
|
172
184
|
/**
|
|
173
185
|
* Remove a CSS property.
|
|
186
|
+
*
|
|
187
|
+
* `styleSheetId = undefined` refers to the inline styles.
|
|
174
188
|
*/
|
|
175
189
|
export interface RemoveGlassEaselStyleSheetProperty extends RequestResponse {
|
|
176
190
|
request: {
|
|
177
|
-
|
|
191
|
+
nodeId: NodeId;
|
|
192
|
+
styleSheetId?: StyleSheetId;
|
|
178
193
|
ruleIndex: number;
|
|
179
194
|
propertyIndex: number;
|
|
180
195
|
};
|
|
181
196
|
}
|
|
182
197
|
/**
|
|
183
198
|
* Replace a CSS property.
|
|
199
|
+
*
|
|
200
|
+
* `styleSheetId = undefined` refers to the inline styles.
|
|
184
201
|
*/
|
|
185
202
|
export interface ReplaceGlassEaselStyleSheetProperty extends RequestResponse {
|
|
186
203
|
request: {
|
|
187
|
-
|
|
204
|
+
nodeId: NodeId;
|
|
205
|
+
styleSheetId?: StyleSheetId;
|
|
188
206
|
ruleIndex: number;
|
|
189
207
|
propertyIndex: number;
|
|
190
208
|
styleText: string;
|
|
@@ -192,20 +210,14 @@ export interface ReplaceGlassEaselStyleSheetProperty extends RequestResponse {
|
|
|
192
210
|
}
|
|
193
211
|
/**
|
|
194
212
|
* Replace all CSS properties.
|
|
213
|
+
*
|
|
214
|
+
* `styleSheetId = undefined` refers to the inline styles.
|
|
195
215
|
*/
|
|
196
216
|
export interface ReplaceGlassEaselStyleSheetAllProperties extends RequestResponse {
|
|
197
|
-
request: {
|
|
198
|
-
styleSheetId: StyleSheetId;
|
|
199
|
-
ruleIndex: number;
|
|
200
|
-
styleText: string;
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Replace inline style for a node.
|
|
205
|
-
*/
|
|
206
|
-
export interface ReplaceGlassEaselStyleSheetInlineStyle extends RequestResponse {
|
|
207
217
|
request: {
|
|
208
218
|
nodeId: NodeId;
|
|
219
|
+
styleSheetId?: StyleSheetId;
|
|
220
|
+
ruleIndex: number;
|
|
209
221
|
styleText: string;
|
|
210
222
|
};
|
|
211
223
|
}
|
package/dist/protocol/dom.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export type AgentRequestKind = {
|
|
|
16
16
|
pushNodesByBackendIdsToFrontend: PushNodesByBackendIdsToFrontend;
|
|
17
17
|
removeAttribute: RemoveAttribute;
|
|
18
18
|
setAttributeValue: SetAttributeValue;
|
|
19
|
+
setGlassEaselClassList: SetGlassEaselClassList;
|
|
19
20
|
setAttributesAsText: SetAttributesAsText;
|
|
20
21
|
getAttributes: GetAttributes;
|
|
21
22
|
getGlassEaselAttributes: GetGlassEaselAttributes;
|
|
@@ -161,9 +162,30 @@ export interface SetAttributeValue extends RequestResponse {
|
|
|
161
162
|
nodeId: NodeId;
|
|
162
163
|
name: string;
|
|
163
164
|
value: string;
|
|
165
|
+
nameType?: 'auto' | 'normal-attribute' | 'property' | 'external-class';
|
|
164
166
|
};
|
|
165
167
|
cdpRequestResponse: [Protocol.DOM.SetAttributeValueRequest, unknown];
|
|
166
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Set an attribute.
|
|
171
|
+
*/
|
|
172
|
+
export interface SetGlassEaselClassList extends RequestResponse {
|
|
173
|
+
request: {
|
|
174
|
+
nodeId: NodeId;
|
|
175
|
+
externalClass?: string;
|
|
176
|
+
classes: {
|
|
177
|
+
className: string;
|
|
178
|
+
disabled?: boolean;
|
|
179
|
+
}[];
|
|
180
|
+
};
|
|
181
|
+
response: {
|
|
182
|
+
classes: {
|
|
183
|
+
className: string;
|
|
184
|
+
disabled?: boolean;
|
|
185
|
+
}[];
|
|
186
|
+
};
|
|
187
|
+
cdpRequestResponse: [unknown, unknown];
|
|
188
|
+
}
|
|
167
189
|
/**
|
|
168
190
|
* Set attributes as text.
|
|
169
191
|
*
|
|
@@ -205,7 +227,10 @@ export interface GetGlassEaselAttributes extends RequestResponse {
|
|
|
205
227
|
virtual: boolean;
|
|
206
228
|
is: string;
|
|
207
229
|
id: string;
|
|
208
|
-
|
|
230
|
+
classes: {
|
|
231
|
+
className: string;
|
|
232
|
+
disabled?: boolean;
|
|
233
|
+
}[];
|
|
209
234
|
slot: string;
|
|
210
235
|
slotName: string | undefined;
|
|
211
236
|
slotValues: {
|
|
@@ -229,7 +254,10 @@ export interface GetGlassEaselAttributes extends RequestResponse {
|
|
|
229
254
|
}[];
|
|
230
255
|
externalClasses?: {
|
|
231
256
|
name: string;
|
|
232
|
-
value:
|
|
257
|
+
value: {
|
|
258
|
+
className: string;
|
|
259
|
+
disabled?: boolean;
|
|
260
|
+
}[];
|
|
233
261
|
}[];
|
|
234
262
|
dataset: {
|
|
235
263
|
name: string;
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glass-easel-devtools-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"peerDependencies": {
|
|
6
|
-
"glass-easel": "^0.
|
|
6
|
+
"glass-easel": "^0.10.2"
|
|
7
7
|
},
|
|
8
8
|
"devDependencies": {
|
|
9
9
|
"@csstools/css-tokenizer": "^2.4.1",
|
|
10
10
|
"@csstools/selector-specificity": "^3.1.1",
|
|
11
|
-
"@types/node": "^20.
|
|
11
|
+
"@types/node": "^20.16.5",
|
|
12
12
|
"devtools-protocol": "^0.0.1319565",
|
|
13
|
-
"glass-easel": "^0.
|
|
14
|
-
"glass-easel-template-compiler": "^0.
|
|
15
|
-
"postcss-selector-parser": "^6.1.
|
|
13
|
+
"glass-easel": "^0.10.2",
|
|
14
|
+
"glass-easel-template-compiler": "^0.10.2",
|
|
15
|
+
"postcss-selector-parser": "^6.1.2"
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "webpack --config webpack.config.js",
|
package/src/backend.ts
CHANGED
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
import * as glassEasel from 'glass-easel'
|
|
4
4
|
import parser from 'postcss-selector-parser'
|
|
5
5
|
import { selectorSpecificity, compare as selectorCompare } from '@csstools/selector-specificity'
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
tokenize,
|
|
8
|
+
TokenType,
|
|
9
|
+
stringify,
|
|
10
|
+
type CSSToken,
|
|
11
|
+
type TokenString,
|
|
12
|
+
} from '@csstools/css-tokenizer'
|
|
7
13
|
import { backendUnsupported } from './utils'
|
|
8
14
|
|
|
9
15
|
export type BoundingClientRect = glassEasel.BoundingClientRect
|
|
@@ -141,42 +147,91 @@ export const getBoxModel = (
|
|
|
141
147
|
})
|
|
142
148
|
}
|
|
143
149
|
|
|
150
|
+
const expectToken = (
|
|
151
|
+
token: CSSToken | undefined,
|
|
152
|
+
type: TokenType,
|
|
153
|
+
name?: string,
|
|
154
|
+
): string | null => {
|
|
155
|
+
if (token === undefined) return null
|
|
156
|
+
if (token[0] !== type) return null
|
|
157
|
+
if (name === undefined) return token[1]
|
|
158
|
+
if (name === token[1]) return name
|
|
159
|
+
return null
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const parseNameValueStr = (cssText: string) => {
|
|
163
|
+
const ret: { name: string; value: string; disabled: boolean }[] = []
|
|
164
|
+
const tokens = tokenize({ css: cssText })
|
|
165
|
+
if (tokens.length === 0) return null
|
|
166
|
+
let nameStart = 0
|
|
167
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
168
|
+
const t = tokens[i]
|
|
169
|
+
if (expectToken(t, TokenType.Colon)) {
|
|
170
|
+
const name = stringify(...tokens.slice(nameStart, i)).trim()
|
|
171
|
+
i += 1
|
|
172
|
+
const valueStart = i
|
|
173
|
+
for (; i < tokens.length; i += 1) {
|
|
174
|
+
const t = tokens[i]
|
|
175
|
+
if (expectToken(t, TokenType.Semicolon)) break
|
|
176
|
+
}
|
|
177
|
+
const value = stringify(...tokens.slice(valueStart, i)).trim()
|
|
178
|
+
ret.push({ name, value, disabled: false })
|
|
179
|
+
nameStart = i + 1
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return ret
|
|
183
|
+
}
|
|
184
|
+
|
|
144
185
|
export const getMatchedRules = (
|
|
145
|
-
|
|
146
|
-
elem: glassEasel.GeneralBackendElement,
|
|
186
|
+
element: glassEasel.Element,
|
|
147
187
|
): Promise<{
|
|
148
188
|
inline: glassEasel.CSSProperty[]
|
|
149
189
|
inlineText?: string
|
|
150
190
|
rules: glassEasel.CSSRule[]
|
|
151
191
|
crossOriginFailing?: boolean
|
|
152
192
|
}> => {
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
193
|
+
const convertAdapterGeneratedRules = (rules: glassEasel.CSSRule[]) => {
|
|
194
|
+
rules.forEach((rule) => {
|
|
195
|
+
const tokens = tokenize({ css: rule.selector })
|
|
196
|
+
if (
|
|
197
|
+
expectToken(tokens[0], TokenType.OpenSquare) &&
|
|
198
|
+
expectToken(tokens[1], TokenType.Ident, 'wx-host') &&
|
|
199
|
+
expectToken(tokens[2], TokenType.Delim, '=') &&
|
|
200
|
+
expectToken(tokens[3], TokenType.String) &&
|
|
201
|
+
expectToken(tokens[4], TokenType.CloseSquare) &&
|
|
202
|
+
expectToken(tokens[5], TokenType.EOF) !== null
|
|
203
|
+
) {
|
|
204
|
+
// convert host rules
|
|
205
|
+
rule.selector = ':host'
|
|
206
|
+
rule.styleScope = (tokens[3] as TokenString)[4].value
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
210
|
+
// convert class prefixes
|
|
211
|
+
const t = tokens[i]
|
|
212
|
+
if (expectToken(t, TokenType.Delim, '.')) {
|
|
213
|
+
const peek = expectToken(tokens[i + 1], TokenType.Ident)
|
|
214
|
+
if (peek) {
|
|
215
|
+
i += 1
|
|
216
|
+
const [prefix, name] = peek.split('--', 2)
|
|
217
|
+
if (name !== undefined) {
|
|
218
|
+
rule.styleScope = prefix
|
|
219
|
+
tokens[i][1] = name
|
|
220
|
+
}
|
|
221
|
+
}
|
|
167
222
|
}
|
|
168
|
-
const value = stringify(...tokens.slice(valueStart, i))
|
|
169
|
-
ret.push({ name, value })
|
|
170
|
-
nameStart = i + 1
|
|
171
223
|
}
|
|
172
|
-
|
|
173
|
-
|
|
224
|
+
rule.selector = stringify(...tokens)
|
|
225
|
+
})
|
|
174
226
|
}
|
|
175
|
-
|
|
227
|
+
|
|
228
|
+
const calcRuleWeight = (
|
|
229
|
+
ctx: glassEasel.GeneralBackendContext,
|
|
230
|
+
rules: glassEasel.CSSRule[],
|
|
231
|
+
): glassEasel.CSSRule[] => {
|
|
176
232
|
const rulesWithSelector = rules.map((rule) => {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
233
|
+
const edit = styleEditContext.createOrGetRule(ctx, rule, rule.propertyText)
|
|
234
|
+
rule.properties = edit.getProps()
|
|
180
235
|
const ps = parser().astSync(rule.selector)
|
|
181
236
|
const specificity = selectorSpecificity(ps)
|
|
182
237
|
return [specificity, rule] as const
|
|
@@ -197,15 +252,22 @@ export const getMatchedRules = (
|
|
|
197
252
|
})
|
|
198
253
|
return rulesWithSelector.map(([_sel, rule]) => rule).reverse()
|
|
199
254
|
}
|
|
255
|
+
|
|
200
256
|
return new Promise((resolve) => {
|
|
257
|
+
const ctx = element.getBackendContext()
|
|
258
|
+
const elem = element.getBackendElement()
|
|
259
|
+
if (!ctx || !elem) {
|
|
260
|
+
resolve({ inline: [], rules: [] })
|
|
261
|
+
return
|
|
262
|
+
}
|
|
201
263
|
if (ctx.mode === glassEasel.BackendMode.Domlike) {
|
|
202
264
|
if (typeof ctx.getMatchedRules === 'function') {
|
|
203
265
|
try {
|
|
204
266
|
ctx.getMatchedRules(elem as glassEasel.domlikeBackend.Element, (ret) => {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
267
|
+
convertAdapterGeneratedRules(ret.rules)
|
|
268
|
+
ret.rules = calcRuleWeight(ctx, ret.rules)
|
|
269
|
+
const edit = styleEditContext.createOrGetInline(elem, ret.inline, ret.inlineText)
|
|
270
|
+
ret.inline = edit.getProps()
|
|
209
271
|
resolve(ret)
|
|
210
272
|
})
|
|
211
273
|
} catch (err) {
|
|
@@ -222,7 +284,9 @@ export const getMatchedRules = (
|
|
|
222
284
|
} else {
|
|
223
285
|
if ('getMatchedRules' in elem) {
|
|
224
286
|
;(elem as glassEasel.backend.Element).getMatchedRules!((ret) => {
|
|
225
|
-
ret.rules = calcRuleWeight(ret.rules)
|
|
287
|
+
ret.rules = calcRuleWeight(ctx, ret.rules)
|
|
288
|
+
const edit = styleEditContext.createOrGetInline(elem, ret.inline, ret.inlineText)
|
|
289
|
+
ret.inline = edit.getProps()
|
|
226
290
|
resolve(ret)
|
|
227
291
|
})
|
|
228
292
|
} else {
|
|
@@ -231,3 +295,243 @@ export const getMatchedRules = (
|
|
|
231
295
|
}
|
|
232
296
|
})
|
|
233
297
|
}
|
|
298
|
+
|
|
299
|
+
const filterCssProperties = (props: glassEasel.CSSProperty[]) => {
|
|
300
|
+
return props.map((prop) => ({
|
|
301
|
+
name: prop.name.trim(),
|
|
302
|
+
value: prop.value.trim(),
|
|
303
|
+
disabled: false,
|
|
304
|
+
important: prop.important,
|
|
305
|
+
}))
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export class StyleRuleEdit {
|
|
309
|
+
private props: { name: string; value: string; disabled: boolean; important?: boolean }[]
|
|
310
|
+
|
|
311
|
+
constructor(props: glassEasel.CSSProperty[]) {
|
|
312
|
+
this.props = filterCssProperties(props)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
static fromMaybeInlineStyle(props: glassEasel.CSSProperty[], inlineStyle?: string) {
|
|
316
|
+
if (inlineStyle === undefined) return new StyleRuleEdit(props)
|
|
317
|
+
const ret = new StyleRuleEdit([])
|
|
318
|
+
ret.props = parseNameValueStr(inlineStyle) ?? []
|
|
319
|
+
return ret
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
updateWithProps(props: glassEasel.CSSProperty[], inlineStyle?: string) {
|
|
323
|
+
const oldProps = this.props
|
|
324
|
+
this.props =
|
|
325
|
+
inlineStyle === undefined ? filterCssProperties(props) : parseNameValueStr(inlineStyle) ?? []
|
|
326
|
+
let i = 0
|
|
327
|
+
oldProps.forEach((prop) => {
|
|
328
|
+
if (prop.disabled) {
|
|
329
|
+
this.props.splice(i, 0, prop)
|
|
330
|
+
i += 1
|
|
331
|
+
}
|
|
332
|
+
while (i < this.props.length) {
|
|
333
|
+
i += 1
|
|
334
|
+
if (prop.name === this.props[i - 1].name) {
|
|
335
|
+
break
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
})
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
getProps() {
|
|
342
|
+
return this.props.slice()
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
clear() {
|
|
346
|
+
this.props.length = 0
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
countProps() {
|
|
350
|
+
return this.props.length
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
append(inlineStyle: string) {
|
|
354
|
+
const list = parseNameValueStr(inlineStyle) ?? []
|
|
355
|
+
this.props.push(...list)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
setDisabled(index: number, disabled: boolean): boolean {
|
|
359
|
+
if (index >= this.props.length) return false
|
|
360
|
+
this.props[index].disabled = disabled
|
|
361
|
+
return true
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
remove(index: number): boolean {
|
|
365
|
+
if (index >= this.props.length) return false
|
|
366
|
+
this.props.splice(index, 1)
|
|
367
|
+
return true
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
replace(index: number, inlineStyle: string): boolean {
|
|
371
|
+
if (index >= this.props.length) return false
|
|
372
|
+
const list = parseNameValueStr(inlineStyle) ?? []
|
|
373
|
+
this.props.splice(index, 1, ...list)
|
|
374
|
+
return true
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
stringify(): string {
|
|
378
|
+
return this.props
|
|
379
|
+
.map(({ name, value, important, disabled }) => {
|
|
380
|
+
if (disabled) return ''
|
|
381
|
+
if (important) return `${name}: ${value} !important;\n`
|
|
382
|
+
return `${name}: ${value};\n`
|
|
383
|
+
})
|
|
384
|
+
.join('')
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
class StyleEditContext {
|
|
389
|
+
inlineStyleMap = new WeakMap<glassEasel.GeneralBackendElement, StyleRuleEdit>()
|
|
390
|
+
ruleMap = new WeakMap<glassEasel.GeneralBackendContext, Record<string, StyleRuleEdit>>()
|
|
391
|
+
|
|
392
|
+
createOrGetInline(
|
|
393
|
+
elem: glassEasel.GeneralBackendElement,
|
|
394
|
+
properties: glassEasel.CSSProperty[],
|
|
395
|
+
inlineStyle?: string,
|
|
396
|
+
): StyleRuleEdit {
|
|
397
|
+
const r = this.inlineStyleMap.get(elem)
|
|
398
|
+
if (r) {
|
|
399
|
+
r.updateWithProps(properties, inlineStyle)
|
|
400
|
+
return r
|
|
401
|
+
}
|
|
402
|
+
const edit = StyleRuleEdit.fromMaybeInlineStyle(properties, inlineStyle)
|
|
403
|
+
this.inlineStyleMap.set(elem, edit)
|
|
404
|
+
return edit
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
createOrGetRule(
|
|
408
|
+
ctx: glassEasel.GeneralBackendContext,
|
|
409
|
+
rule: glassEasel.CSSRule,
|
|
410
|
+
inlineStyle?: string,
|
|
411
|
+
): StyleRuleEdit {
|
|
412
|
+
const key = `${rule.sheetIndex}/${rule.ruleIndex}`
|
|
413
|
+
const map = this.ruleMap.get(ctx)
|
|
414
|
+
if (!map) {
|
|
415
|
+
const map = Object.create(null) as Record<string, StyleRuleEdit>
|
|
416
|
+
map[key] = StyleRuleEdit.fromMaybeInlineStyle(rule.properties, inlineStyle)
|
|
417
|
+
this.ruleMap.set(ctx, map)
|
|
418
|
+
return map[key]
|
|
419
|
+
}
|
|
420
|
+
if (!map[key]) {
|
|
421
|
+
map[key] = StyleRuleEdit.fromMaybeInlineStyle(rule.properties, inlineStyle)
|
|
422
|
+
return map[key]
|
|
423
|
+
}
|
|
424
|
+
map[key].updateWithProps(rule.properties, inlineStyle)
|
|
425
|
+
return map[key]
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
updateInline(
|
|
429
|
+
ctx: glassEasel.GeneralBackendContext,
|
|
430
|
+
elem: glassEasel.GeneralBackendElement,
|
|
431
|
+
f: (edit: StyleRuleEdit) => void,
|
|
432
|
+
) {
|
|
433
|
+
const edit = this.inlineStyleMap.get(elem)
|
|
434
|
+
if (!edit) return
|
|
435
|
+
f(edit)
|
|
436
|
+
const style = edit.stringify()
|
|
437
|
+
if (ctx.mode === glassEasel.BackendMode.Domlike) {
|
|
438
|
+
;(elem as glassEasel.domlikeBackend.Element).setAttribute('style', style)
|
|
439
|
+
} else if (ctx.mode === glassEasel.BackendMode.Composed) {
|
|
440
|
+
;(elem as glassEasel.composedBackend.Element).setStyle(style)
|
|
441
|
+
} else if (ctx.mode === glassEasel.BackendMode.Shadow) {
|
|
442
|
+
;(elem as glassEasel.backend.Element).setStyle(style)
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
updateRule(
|
|
447
|
+
ctx: glassEasel.GeneralBackendContext,
|
|
448
|
+
sheetIndex: number,
|
|
449
|
+
ruleIndex: number,
|
|
450
|
+
f: (edit: StyleRuleEdit) => void,
|
|
451
|
+
): Promise<void> {
|
|
452
|
+
const key = `${sheetIndex}/${ruleIndex}`
|
|
453
|
+
const edit = this.ruleMap.get(ctx)?.[key]
|
|
454
|
+
if (!edit) return Promise.resolve()
|
|
455
|
+
f(edit)
|
|
456
|
+
const style = edit.stringify()
|
|
457
|
+
return new Promise((resolve) => {
|
|
458
|
+
ctx.replaceStyleSheetAllProperties?.(sheetIndex, ruleIndex, style, () => {
|
|
459
|
+
resolve()
|
|
460
|
+
})
|
|
461
|
+
})
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export const styleEditContext = new StyleEditContext()
|
|
466
|
+
|
|
467
|
+
export class ClassListEdit {
|
|
468
|
+
private list: { className: string; disabled: boolean }[] = []
|
|
469
|
+
|
|
470
|
+
update(names: string[]) {
|
|
471
|
+
const oldList = this.list
|
|
472
|
+
this.list = names.map((className) => ({ className, disabled: false }))
|
|
473
|
+
let i = 0
|
|
474
|
+
oldList.forEach((item) => {
|
|
475
|
+
if (item.disabled) {
|
|
476
|
+
this.list.splice(i, 0, item)
|
|
477
|
+
i += 1
|
|
478
|
+
}
|
|
479
|
+
while (i < this.list.length) {
|
|
480
|
+
i += 1
|
|
481
|
+
if (item.className === this.list[i - 1].className) {
|
|
482
|
+
break
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
})
|
|
486
|
+
return this
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
setDisabled(className: string, disabled: boolean) {
|
|
490
|
+
const item = this.list.find((x) => x.className === className)
|
|
491
|
+
if (item) item.disabled = disabled
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
getClasses() {
|
|
495
|
+
return this.list.slice()
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
setClasses(list: { className: string; disabled?: boolean }[]) {
|
|
499
|
+
this.list = []
|
|
500
|
+
list.forEach(({ className, disabled }) => {
|
|
501
|
+
className.split(/\s+/g).forEach((className) => {
|
|
502
|
+
if (!className) return
|
|
503
|
+
this.list.push({ className, disabled: !!disabled })
|
|
504
|
+
})
|
|
505
|
+
})
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
stringify() {
|
|
509
|
+
return this.list
|
|
510
|
+
.filter((x) => !x.disabled)
|
|
511
|
+
.map((x) => x.className)
|
|
512
|
+
.join(' ')
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
matches(v: string): boolean {
|
|
516
|
+
return this.stringify() === v
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
export class ClassEditContext {
|
|
521
|
+
map = new WeakMap<glassEasel.Element, Record<string, ClassListEdit>>()
|
|
522
|
+
|
|
523
|
+
createOrGet(external: string, elem: glassEasel.Element): ClassListEdit {
|
|
524
|
+
if (!this.map.get(elem)) {
|
|
525
|
+
const group = Object.create(null) as Record<string, ClassListEdit>
|
|
526
|
+
this.map.set(elem, group)
|
|
527
|
+
}
|
|
528
|
+
const group = this.map.get(elem)!
|
|
529
|
+
const key = external ?? ''
|
|
530
|
+
if (!group[key]) {
|
|
531
|
+
group[key] = new ClassListEdit()
|
|
532
|
+
}
|
|
533
|
+
return group[key]
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
export const classEditContext = new ClassEditContext()
|