inline-style-editor 1.3.7 → 1.4.0
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/README.md +3 -0
- package/dist/inline-style-editor.css +1 -1
- package/dist/inline-style-editor.js +5 -4
- package/dist/inline-style-editor.js.map +1 -1
- package/dist/inline-style-editor.mjs +792 -452
- package/dist/inline-style-editor.mjs.map +1 -1
- package/index.d.ts +38 -0
- package/package.json +18 -3
- package/docs/index.html +0 -384
- package/rollup.config.js +0 -41
- package/src/assets/index.scss +0 -2
- package/src/assets/style.scss +0 -152
- package/src/components/ColorPicker.svelte +0 -52
- package/src/components/InlineStyleEditor.svelte +0 -498
- package/src/index.js +0 -7
- package/src/util/boxesContour.js +0 -91
- package/src/util/fonts.js +0 -89
- package/src/util/path.js +0 -46
- package/src/util/util.js +0 -23
- package/stats.html +0 -4034
- package/test.html +0 -409
package/src/assets/style.scss
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
.ise {
|
|
2
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
|
3
|
-
font-size: 14px;
|
|
4
|
-
z-index: 9999999;
|
|
5
|
-
position: absolute;
|
|
6
|
-
background-color: #edf2f7;
|
|
7
|
-
max-width: 300px;
|
|
8
|
-
border-radius: 5px;
|
|
9
|
-
box-shadow: 0px 8px 17px 2px #0000007c;
|
|
10
|
-
select {
|
|
11
|
-
appearance: none;
|
|
12
|
-
-webkit-appearance: none;
|
|
13
|
-
-moz-appearance: none;
|
|
14
|
-
font-size: inherit;
|
|
15
|
-
font-family: inherit;
|
|
16
|
-
padding: 0.5rem;
|
|
17
|
-
color: #212121;
|
|
18
|
-
background: white;
|
|
19
|
-
border: 1px solid #d8dae1;
|
|
20
|
-
border-radius: 5px;
|
|
21
|
-
box-shadow: none;
|
|
22
|
-
max-width: 100%;
|
|
23
|
-
display: inline-block;
|
|
24
|
-
}
|
|
25
|
-
select:not([multiple]) {
|
|
26
|
-
background-image: linear-gradient(45deg, transparent 49%, #212121 51%),
|
|
27
|
-
linear-gradient(135deg, #212121 51%, transparent 49%);
|
|
28
|
-
background-position: calc(100% - 15px), calc(100% - 10px);
|
|
29
|
-
background-size: 5px 5px, 5px 5px;
|
|
30
|
-
background-repeat: no-repeat;
|
|
31
|
-
padding: 5px 25px 5px 5px;
|
|
32
|
-
}
|
|
33
|
-
input[type="range"] {
|
|
34
|
-
width: 100%;
|
|
35
|
-
margin: auto;
|
|
36
|
-
}
|
|
37
|
-
.delete {
|
|
38
|
-
cursor: pointer;
|
|
39
|
-
font-size: 20px;
|
|
40
|
-
}
|
|
41
|
-
.select-tab {
|
|
42
|
-
display: flex;
|
|
43
|
-
align-items: center;
|
|
44
|
-
background-color: #edf2f7;
|
|
45
|
-
padding: 5px 0 5px 0;
|
|
46
|
-
margin: 0 10px 0 10px;
|
|
47
|
-
border-bottom: 1px solid #dee2e6;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.select-tab > span {
|
|
51
|
-
text-overflow: ellipsis;
|
|
52
|
-
overflow: hidden;
|
|
53
|
-
white-space: nowrap;
|
|
54
|
-
min-width: 50px;
|
|
55
|
-
padding: 3px;
|
|
56
|
-
text-align: center;
|
|
57
|
-
color: #718096;
|
|
58
|
-
cursor: pointer;
|
|
59
|
-
}
|
|
60
|
-
.select-tab > b {
|
|
61
|
-
margin-right: 5px;
|
|
62
|
-
color: #5d5d5d;
|
|
63
|
-
}
|
|
64
|
-
.select-tab span.selected {
|
|
65
|
-
border-radius: 2px;
|
|
66
|
-
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px 0 rgba(0, 0, 0, .06);
|
|
67
|
-
color: #0069d9;
|
|
68
|
-
background-color: white;
|
|
69
|
-
}
|
|
70
|
-
.editor {
|
|
71
|
-
padding: 5px;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
.editor .prop-section {
|
|
75
|
-
display: flex;
|
|
76
|
-
align-items: center;
|
|
77
|
-
margin: 5px 0;
|
|
78
|
-
align-items: baseline;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.editor .prop-section>span {
|
|
82
|
-
margin: 0 5px;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
.editor .prop-section :first-child {
|
|
86
|
-
color: #5d5d5d;
|
|
87
|
-
font-weight: bold;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
.editor .btn {
|
|
91
|
-
&.active {
|
|
92
|
-
background-color: #4d84bf;
|
|
93
|
-
}
|
|
94
|
-
&.delete-elem {
|
|
95
|
-
background-color: #d13737;
|
|
96
|
-
}
|
|
97
|
-
margin: 0 5px;
|
|
98
|
-
background-color: #3333330d;
|
|
99
|
-
border-radius: 4px;
|
|
100
|
-
border: 1px solid #d8dae1;
|
|
101
|
-
color: #333333;
|
|
102
|
-
cursor: pointer;
|
|
103
|
-
display: inline-block;
|
|
104
|
-
padding: 5px;
|
|
105
|
-
}
|
|
106
|
-
.close-button {
|
|
107
|
-
position: absolute;
|
|
108
|
-
top: -7px;
|
|
109
|
-
right: -7px;
|
|
110
|
-
background-color: #dbdbdb;
|
|
111
|
-
color: #818181;
|
|
112
|
-
width: 15px;
|
|
113
|
-
height: 15px;
|
|
114
|
-
display: flex;
|
|
115
|
-
justify-content: center;
|
|
116
|
-
align-items: center;
|
|
117
|
-
cursor: pointer;
|
|
118
|
-
border-radius: 3px;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
.layout_default.picker_wrapper {
|
|
122
|
-
background: none;
|
|
123
|
-
box-shadow: none;
|
|
124
|
-
width: 17em;
|
|
125
|
-
|
|
126
|
-
.picker_done {
|
|
127
|
-
display: none;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
.picker_slider,
|
|
131
|
-
.picker_selector {
|
|
132
|
-
padding: 0.5em;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
.picker_editor input {
|
|
136
|
-
font-size: 0.7rem;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
.ise-helper-wrapper {
|
|
142
|
-
z-index: 9999998;
|
|
143
|
-
position: absolute;
|
|
144
|
-
top: 0;
|
|
145
|
-
left: 0;
|
|
146
|
-
pointer-events: none;
|
|
147
|
-
.overlay-over {
|
|
148
|
-
fill: #000000A0;
|
|
149
|
-
clip-path: url(#overlay-clip);
|
|
150
|
-
pointer-events: painted;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import Picker from 'vanilla-picker/csp';
|
|
3
|
-
|
|
4
|
-
import { onMount, onDestroy } from 'svelte';
|
|
5
|
-
|
|
6
|
-
export let value = "#AAAAAAFF";
|
|
7
|
-
export let options = {};
|
|
8
|
-
export let onChange = () => {};
|
|
9
|
-
let self;
|
|
10
|
-
let pickerElem;
|
|
11
|
-
|
|
12
|
-
$: if (pickerElem) {
|
|
13
|
-
pickerElem.setColor(value);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function setColor(rgbaString) {
|
|
17
|
-
pickerElem.setColor(rgbaString);
|
|
18
|
-
}
|
|
19
|
-
function setValue(val) {
|
|
20
|
-
if (val === value) return;
|
|
21
|
-
onChange(val, value)
|
|
22
|
-
value = val;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function _onChange(color) {
|
|
26
|
-
setValue(color.hex);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
onMount( () => {
|
|
30
|
-
init(options);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
onDestroy( () => {
|
|
34
|
-
pickerElem.destroy();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
function init(opts) {
|
|
38
|
-
if (!self) return;
|
|
39
|
-
if (pickerElem) pickerElem.destroy();
|
|
40
|
-
opts.onChange = _onChange;
|
|
41
|
-
pickerElem = new Picker({
|
|
42
|
-
parent: self,
|
|
43
|
-
color: value,
|
|
44
|
-
popup: false,
|
|
45
|
-
...opts
|
|
46
|
-
});
|
|
47
|
-
pickerElem.show();
|
|
48
|
-
pickerElem.openHandler();
|
|
49
|
-
}
|
|
50
|
-
</script>
|
|
51
|
-
|
|
52
|
-
<div bind:this={self} ></div>
|
|
@@ -1,498 +0,0 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import { onMount, onDestroy, tick } from "svelte";
|
|
3
|
-
|
|
4
|
-
import '../assets/index.scss';
|
|
5
|
-
import { pick, debounce } from '../util/util';
|
|
6
|
-
import { computeContours } from '../util/boxesContour';
|
|
7
|
-
import ColorPicker from './ColorPicker.svelte';
|
|
8
|
-
import { getFonts } from '../util/fonts';
|
|
9
|
-
const strokeElements = [ "altGlyph", "circle", "ellipse", "line", "path", "polygon", "polyline", "rect", "text", "textPath", "tref", "tspan",];
|
|
10
|
-
|
|
11
|
-
const borderProps = ["border-radius", "border-width", "border-color", "border-style"];
|
|
12
|
-
const backgroundProps = ["background-color"];
|
|
13
|
-
const fontProps = ["font-family", "font-size", "font-weight", "color"];
|
|
14
|
-
const pathProps = ["stroke-width", "stroke", "stroke-dasharray", "stroke-linejoin", "fill"];
|
|
15
|
-
const cssPropByType = {
|
|
16
|
-
"border-radius": {type: "slider", min: 0, max: 30, suffix: 'px'},
|
|
17
|
-
"border-width": {type: "slider", min: 0, max: 30, suffix: 'px'},
|
|
18
|
-
"border-style": {type: 'select', choices: () => ["none", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset",]},
|
|
19
|
-
"border-color": {type: "color"},
|
|
20
|
-
"font-family": { type: 'select', choices: getFontFamilies},
|
|
21
|
-
"font-size": {type: "slider", min: 0, max: 40, suffix: 'px'},
|
|
22
|
-
"font-weight": {type: "slider", min: 0, max: 800},
|
|
23
|
-
"color": {type: "color"},
|
|
24
|
-
"stroke-width": {type: "slider", min: 0, max: 20, step: 0.5, suffix: 'px'},
|
|
25
|
-
'stroke': {type: "color"},
|
|
26
|
-
"stroke-linejoin": { type: 'select', choices: () => ["bevel", "miter", "round"]},
|
|
27
|
-
'fill': {type: "color"},
|
|
28
|
-
"stroke-dasharray": {type: "slider", min: 0, max: 30, suffix: 'px'},
|
|
29
|
-
"background-color": {type: "color"},
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
export let getElems = null;
|
|
34
|
-
export let listenOnClick = false;
|
|
35
|
-
export let onStyleChanged = () => {};
|
|
36
|
-
export let customProps = {};
|
|
37
|
-
export let inlineDeletable = () => true;
|
|
38
|
-
export let cssRuleFilter = null;
|
|
39
|
-
const typeText = "text";
|
|
40
|
-
const typeBorder = "border";
|
|
41
|
-
const typeStroke = "stroke";
|
|
42
|
-
const typeBackground = "background";
|
|
43
|
-
const customType = "custom";
|
|
44
|
-
const propByType = {
|
|
45
|
-
[typeText]: fontProps,
|
|
46
|
-
[typeBorder]: borderProps,
|
|
47
|
-
[typeStroke]: pathProps,
|
|
48
|
-
[typeBackground]: backgroundProps,
|
|
49
|
-
[customType]: Object.keys(customProps),
|
|
50
|
-
};
|
|
51
|
-
const inputTypeOrder = {slider: 0, select: 1, color: 2};
|
|
52
|
-
|
|
53
|
-
let elementToListen = null;
|
|
54
|
-
let positionAnchor;
|
|
55
|
-
let self;
|
|
56
|
-
let helperElemWrapper;
|
|
57
|
-
let pathWithHoles = '';
|
|
58
|
-
let pageDimensions = { width: 0, height: 0 };
|
|
59
|
-
let targetsToSearch = [[]];
|
|
60
|
-
let allRules = []; // list of list of CSS rules, for every target element
|
|
61
|
-
let allTypes = []; // list of list of types (e.g color, border), for every target element
|
|
62
|
-
let selectedElemIndex = 0;
|
|
63
|
-
let selectedRuleIndex = 0;
|
|
64
|
-
let selectedTypeIndex = 0;
|
|
65
|
-
let propsByType; // propType -> {[props], selected}
|
|
66
|
-
let allCurrentPropDefs = {}; // propName => selectorDef
|
|
67
|
-
let bringableToFront = []; // null = not bringable, true = bringable, false = was bringed
|
|
68
|
-
let hasDisplayedCustom = false;
|
|
69
|
-
$: {
|
|
70
|
-
if (elementToListen !== null) {
|
|
71
|
-
init();
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
$: currentElement = targetsToSearch[selectedElemIndex][0];
|
|
75
|
-
$: currentRule = allRules[selectedElemIndex]?.[selectedRuleIndex];
|
|
76
|
-
let curType;
|
|
77
|
-
$: {
|
|
78
|
-
if (allTypes[selectedElemIndex]?.[selectedTypeIndex] !== curType) {
|
|
79
|
-
curType = allTypes[selectedElemIndex]?.[selectedTypeIndex];
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
$: {
|
|
83
|
-
if (curType || selectedRuleIndex || selectedElemIndex) {
|
|
84
|
-
initAndGroup();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
onMount(() => {
|
|
89
|
-
close();
|
|
90
|
-
elementToListen = self.parentNode
|
|
91
|
-
document.body.appendChild(self);
|
|
92
|
-
document.body.appendChild(helperElemWrapper);
|
|
93
|
-
document.body.appendChild(positionAnchor);
|
|
94
|
-
udpatePageDimensions();
|
|
95
|
-
// make sure the layout is computed to get the client size
|
|
96
|
-
setTimeout(() => {
|
|
97
|
-
udpatePageDimensions();
|
|
98
|
-
}, 1000);
|
|
99
|
-
window.addEventListener('resize', udpatePageDimensions);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
onDestroy(() => {
|
|
103
|
-
window.removeEventListener('resize', udpatePageDimensions);
|
|
104
|
-
if(listenOnClick) elementToListen.removeEventListener("click", getTargetsAndRules);
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
function getFontFamilies() {
|
|
108
|
-
return getFonts();
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function initAndGroup(){
|
|
112
|
-
const allProps = {...cssPropByType, ...customProps};
|
|
113
|
-
const _allCurrentPropDefs = pick(allProps, propByType[curType]);
|
|
114
|
-
Object.keys(_allCurrentPropDefs).forEach(key => {
|
|
115
|
-
const propSelectType = _allCurrentPropDefs[key].type;
|
|
116
|
-
let retrieveType = 'number';
|
|
117
|
-
if (propSelectType === 'color') retrieveType = 'rgb';
|
|
118
|
-
else if (propSelectType === 'select') retrieveType = 'raw';
|
|
119
|
-
if (_allCurrentPropDefs[key].getter) {
|
|
120
|
-
const val = _allCurrentPropDefs[key].getter(currentElement);
|
|
121
|
-
if (val === null) {
|
|
122
|
-
delete _allCurrentPropDefs[key];
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
_allCurrentPropDefs[key].value = val;
|
|
126
|
-
_allCurrentPropDefs[key].displayed = val;
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
_allCurrentPropDefs[key].displayed = getComputedPropValue(currentElement, key, 'raw');
|
|
130
|
-
_allCurrentPropDefs[key].value = getComputedPropValue(currentElement, key, retrieveType);
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
propsByType = Object.entries(_allCurrentPropDefs).reduce((byType, [propName, selectorDef]) => {
|
|
135
|
-
const selectorType = selectorDef.type;
|
|
136
|
-
const existing = byType.find(x => x.type === selectorType);
|
|
137
|
-
if (!existing) byType.push({selected: 0, props: [propName], type: selectorType});
|
|
138
|
-
else existing.props.push(propName);
|
|
139
|
-
return byType;
|
|
140
|
-
}, []).sort((a, b) => {
|
|
141
|
-
if (inputTypeOrder[a.type] < inputTypeOrder[b.type]) return -1;
|
|
142
|
-
if (inputTypeOrder[a.type] > inputTypeOrder[b.type]) return 1;
|
|
143
|
-
return 0;
|
|
144
|
-
});
|
|
145
|
-
allCurrentPropDefs = _allCurrentPropDefs;
|
|
146
|
-
updateHelpers();
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function getRuleNames(rules) {
|
|
150
|
-
if (!rules) return [];
|
|
151
|
-
return rules.map((rule, i) => {
|
|
152
|
-
if (rule === 'inline') return 'inline';
|
|
153
|
-
const cssSelector = rule.selectorText;
|
|
154
|
-
const title = rule.parentStyleSheet.title || `${i}`;
|
|
155
|
-
return `${title}: ${cssSelector}`;
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
let warningDisplayed = new Set();
|
|
160
|
-
function getMatchedCSSRules(elems) {
|
|
161
|
-
const sheets = document.styleSheets;
|
|
162
|
-
return elems.reduce((matchedRulesByElem, elemDef) => {
|
|
163
|
-
const el = elemDef[0];
|
|
164
|
-
const matchedRules = ['inline'];
|
|
165
|
-
for (let i in sheets) {
|
|
166
|
-
try {
|
|
167
|
-
const rules = sheets[i].cssRules;
|
|
168
|
-
for (let r in rules) {
|
|
169
|
-
let selectorText = rules[r].selectorText;
|
|
170
|
-
if (!selectorText || rules[r].selectorText.length > 50) continue; // skip selectors too long
|
|
171
|
-
if (selectorText.split(',').some(selector => selector === '*')) continue; // skip * selector
|
|
172
|
-
if (selectorText.endsWith(':hover')) selectorText = selectorText.substring(0, selectorText.length - ':hover'.length);
|
|
173
|
-
if (el.matches(selectorText)) {
|
|
174
|
-
if (cssRuleFilter !== null && !cssRuleFilter(el, rules[r].selectorText)) continue;
|
|
175
|
-
matchedRules.push(rules[r]);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
} catch(err) {
|
|
179
|
-
if (!warningDisplayed.has(i)) {
|
|
180
|
-
console.warn('Style editor: Not able to access', sheets[i].ownerNode, 'sheet. Try CORS loading the sheet if you want to edit it.');
|
|
181
|
-
warningDisplayed.add(i);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
matchedRulesByElem.push(matchedRules);
|
|
186
|
-
return matchedRulesByElem;
|
|
187
|
-
}, []);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function getEditableTypes(elems) {
|
|
191
|
-
return elems.reduce((typesByElem, elemDef) => {
|
|
192
|
-
const elem = elemDef[0];
|
|
193
|
-
const types = [];
|
|
194
|
-
if (elem.firstChild && (elem.firstChild.nodeType === 3 || elem.firstChild.tagName === 'tspan')) { // Node.TEXT_NODE
|
|
195
|
-
types.push(typeText);
|
|
196
|
-
}
|
|
197
|
-
const elemTagName = elem.tagName.toLowerCase();
|
|
198
|
-
let bringable = false;
|
|
199
|
-
if (strokeElements.includes(elemTagName)) {
|
|
200
|
-
types.push(typeStroke);
|
|
201
|
-
const parentTag = elem.parentElement.tagName.toLowerCase();
|
|
202
|
-
if (parentTag === 'g' && elem.previousElementSibling && elem.previousElementSibling.tagName.toLowerCase() == elemTagName) {
|
|
203
|
-
bringable = true;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
else {
|
|
207
|
-
types.push(typeBorder);
|
|
208
|
-
types.push(typeBackground);
|
|
209
|
-
}
|
|
210
|
-
if (bringable) bringableToFront.push(true);
|
|
211
|
-
else bringableToFront.push(null);
|
|
212
|
-
typesByElem.push(types)
|
|
213
|
-
return typesByElem;
|
|
214
|
-
}, []);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function init() {
|
|
218
|
-
if(listenOnClick) elementToListen.addEventListener("click", _open);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function _open(e) {
|
|
222
|
-
open(e.target, e.pageX, e.pageY);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
export async function open(el, x, y) {
|
|
226
|
-
udpatePageDimensions();
|
|
227
|
-
if (el.classList.contains('overlay-over')) return overlayClicked();
|
|
228
|
-
else if (self.contains(el)) return;
|
|
229
|
-
selectedElemIndex = 0;
|
|
230
|
-
selectedRuleIndex = 0;
|
|
231
|
-
selectedTypeIndex = 0;
|
|
232
|
-
bringableToFront = [];
|
|
233
|
-
allTypes = [];
|
|
234
|
-
allRules = [];
|
|
235
|
-
if (getElems) targetsToSearch = getElems(el);
|
|
236
|
-
else targetsToSearch = [[el, 'Clicked']];
|
|
237
|
-
allTypes = getEditableTypes(targetsToSearch);
|
|
238
|
-
hasDisplayedCustom = false;
|
|
239
|
-
allRules = getMatchedCSSRules(targetsToSearch);
|
|
240
|
-
for (let def of Object.values(customProps)) {
|
|
241
|
-
if (def.getter(el) !== null) {
|
|
242
|
-
hasDisplayedCustom = true;
|
|
243
|
-
break;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
if (Object.keys(customProps).length) {
|
|
247
|
-
allTypes[0].push(customType);
|
|
248
|
-
}
|
|
249
|
-
await tick();
|
|
250
|
-
initAndGroup();
|
|
251
|
-
if (x && y) show(x, y);
|
|
252
|
-
else {
|
|
253
|
-
const rect = getBoundingBoxInfos(el, 15);
|
|
254
|
-
show(rect.left, rect.top);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
export function close() {
|
|
259
|
-
self.style.display = "none";
|
|
260
|
-
helperElemWrapper.style.display = "none";
|
|
261
|
-
pathWithHoles = ''
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
export function isOpened() {
|
|
265
|
-
return self.style.display === 'block';
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function overlayClicked() {
|
|
269
|
-
close();
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
function show(x, y) {
|
|
273
|
-
self.style.display = "block";
|
|
274
|
-
self.style.opacity = 0;
|
|
275
|
-
const popupDimension = self.getBoundingClientRect();
|
|
276
|
-
x = (x + popupDimension.width + 20 > pageDimensions.width) ? x - popupDimension.width - 20: x + 20;
|
|
277
|
-
y = (y + popupDimension.height + 20 > pageDimensions.height) ? y - popupDimension.height - 20 : y + 20;
|
|
278
|
-
y = Math.max(y, 0);
|
|
279
|
-
self.style.left = x + "px";
|
|
280
|
-
self.style.top = y + "px";
|
|
281
|
-
helperElemWrapper.style.display = "block";
|
|
282
|
-
self.style.opacity = 1;
|
|
283
|
-
updateHelpers();
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
async function updateHelpers() {
|
|
287
|
-
await tick();
|
|
288
|
-
if (!currentRule) return;
|
|
289
|
-
let matching;
|
|
290
|
-
if (currentRule === 'inline') matching = [currentElement];
|
|
291
|
-
else {
|
|
292
|
-
const selector = currentRule.selectorText.replace(/(:hover)|:focus/g, '');
|
|
293
|
-
matching = Array.from(document.querySelectorAll(selector));
|
|
294
|
-
}
|
|
295
|
-
const boundingBoxes = matching.map(el => getBoundingBoxInfos(el, 10));
|
|
296
|
-
pathWithHoles = computeContours(boundingBoxes, pageDimensions);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function getBoundingBoxInfos(el, padding = 0) {
|
|
300
|
-
const rect = el.getBoundingClientRect();
|
|
301
|
-
return {
|
|
302
|
-
left: rect.left + window.scrollX - padding,
|
|
303
|
-
top: rect.top + window.scrollY - padding,
|
|
304
|
-
width: rect.width + padding * 2,
|
|
305
|
-
height: rect.height + padding * 2,
|
|
306
|
-
right: rect.left + window.scrollX + rect.width + padding,
|
|
307
|
-
bottom: rect.top + window.scrollY + rect.height + padding,
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
function _updateProp(propName, val, suffix) {
|
|
312
|
-
const finalValue = suffix ? val + suffix : val;
|
|
313
|
-
if (currentRule === 'inline') {
|
|
314
|
-
if (allCurrentPropDefs[propName].setter) {
|
|
315
|
-
allCurrentPropDefs[propName].setter(currentElement, val);
|
|
316
|
-
}
|
|
317
|
-
else {
|
|
318
|
-
const style = currentElement.style; // do not trigger reactivity on currentElement
|
|
319
|
-
style[propName] = finalValue;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
else currentRule.style.setProperty(propName, finalValue);
|
|
323
|
-
allCurrentPropDefs[propName].value = val;
|
|
324
|
-
allCurrentPropDefs[propName].displayed = finalValue;
|
|
325
|
-
|
|
326
|
-
onStyleChanged(currentElement, currentRule, propName, finalValue);
|
|
327
|
-
updateHelpers();
|
|
328
|
-
}
|
|
329
|
-
const updateProp = debounce(_updateProp, 100);
|
|
330
|
-
|
|
331
|
-
function udpatePageDimensions() {
|
|
332
|
-
const bodyStyle = getComputedStyle(document.body);
|
|
333
|
-
const marginLeft = parseInt(bodyStyle.marginLeft);
|
|
334
|
-
const marginRight = parseInt(bodyStyle.marginRight);
|
|
335
|
-
const marginTop = parseInt(bodyStyle.marginTop);
|
|
336
|
-
const marginBottom = parseInt(bodyStyle.marginBottom);
|
|
337
|
-
pageDimensions = {
|
|
338
|
-
width: document.body.offsetWidth + marginLeft + marginRight,
|
|
339
|
-
height: document.body.offsetHeight + marginTop + marginBottom
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
function cssRgbToHex(rgbStr) {
|
|
344
|
-
const m = rgbStr.match(/[0-9\.]+/g).map(i => parseFloat(i));
|
|
345
|
-
if (m.length === 3) m.push(1);
|
|
346
|
-
return m.reduce((hexStr, cur, i) => {
|
|
347
|
-
if (i === 3) hexStr += Math.round(cur * 255).toString(16).padStart(2, '0');
|
|
348
|
-
else hexStr += cur.toString(16).padStart(2, '0');
|
|
349
|
-
return hexStr;
|
|
350
|
-
}, '#');
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// type one of: "number", "rgb", "font", "raw"
|
|
354
|
-
function parsePropvalue(value, type="number") {
|
|
355
|
-
if (type=="raw") return value;
|
|
356
|
-
if (type == "number" && /[0-9]+(px)|(em)|(rem)/.test(value)) return parseInt(value);
|
|
357
|
-
if (type == "rgb") {
|
|
358
|
-
if (value === "none") return "#00000000";
|
|
359
|
-
if ((value.includes('rgb') || value[0] == "#")) return cssRgbToHex(value);
|
|
360
|
-
}
|
|
361
|
-
return value
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
function getComputedPropValue(el, cssProp, type="number") {
|
|
365
|
-
let currentStyleValue = currentRule?.style?.[cssProp];
|
|
366
|
-
if (!currentStyleValue) {
|
|
367
|
-
const computed = getComputedStyle(el);
|
|
368
|
-
currentStyleValue = computed[cssProp];
|
|
369
|
-
}
|
|
370
|
-
return parsePropvalue(currentStyleValue, type);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function bringToFront() {
|
|
374
|
-
bringableToFront[selectedElemIndex] = false;
|
|
375
|
-
currentElement.parentNode.appendChild(currentElement);
|
|
376
|
-
onStyleChanged(currentElement, currentRule, 'bringtofront', null);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
function deleteElem() {
|
|
380
|
-
currentElement.remove();
|
|
381
|
-
close();
|
|
382
|
-
}
|
|
383
|
-
function deleteProp(propName) {
|
|
384
|
-
if (currentRule === 'inline') {
|
|
385
|
-
currentElement.style.removeProperty(propName)
|
|
386
|
-
}
|
|
387
|
-
else {
|
|
388
|
-
currentRule.style.removeProperty(propName);
|
|
389
|
-
}
|
|
390
|
-
onStyleChanged(currentElement, currentRule, propName, null);
|
|
391
|
-
initAndGroup();
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
function selectRule(ruleIndex) {
|
|
395
|
-
const newRule = allRules[selectedElemIndex]?.[ruleIndex];
|
|
396
|
-
if (newRule !== 'inline' && selectedTypeIndex === allTypes[selectedElemIndex].length - 1 ) {
|
|
397
|
-
selectedTypeIndex = 0;
|
|
398
|
-
}
|
|
399
|
-
selectedRuleIndex = ruleIndex;
|
|
400
|
-
}
|
|
401
|
-
</script>
|
|
402
|
-
|
|
403
|
-
<div bind:this={positionAnchor} style="position: absolute;"> </div>
|
|
404
|
-
<svg bind:this={helperElemWrapper} class="ise-helper-wrapper"
|
|
405
|
-
version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
406
|
-
width={pageDimensions.width} height={pageDimensions.height}
|
|
407
|
-
on:click={overlayClicked}>
|
|
408
|
-
<clipPath id="overlay-clip" clip-rule="evenodd">
|
|
409
|
-
<path d={pathWithHoles} />
|
|
410
|
-
</clipPath>
|
|
411
|
-
<rect y="0" x="0" height="100%" width="100%" class="overlay-over" />
|
|
412
|
-
</svg>
|
|
413
|
-
|
|
414
|
-
<div class="ise" bind:this={self}>
|
|
415
|
-
<div class="close-button" on:click={close}>x</div>
|
|
416
|
-
{#if targetsToSearch.length > 1}
|
|
417
|
-
<div class="select-tab">
|
|
418
|
-
<b> Elem </b>
|
|
419
|
-
{#each targetsToSearch as [_, name], elemIndex}
|
|
420
|
-
<span class:selected={selectedElemIndex === elemIndex} on:click={() => {selectedElemIndex = elemIndex; selectedRuleIndex = 0;}}>
|
|
421
|
-
{name}
|
|
422
|
-
</span>
|
|
423
|
-
{/each}
|
|
424
|
-
</div>
|
|
425
|
-
{/if}
|
|
426
|
-
<div class="select-tab">
|
|
427
|
-
<b> Rule: </b>
|
|
428
|
-
{#each getRuleNames(allRules[selectedElemIndex]) as ruleName, ruleIndex}
|
|
429
|
-
<span title={ruleName}
|
|
430
|
-
class:selected="{selectedRuleIndex === ruleIndex}"
|
|
431
|
-
on:click="{() => { selectRule(ruleIndex); }}"
|
|
432
|
-
> {ruleName}</span>
|
|
433
|
-
{/each}
|
|
434
|
-
</div>
|
|
435
|
-
<div class="select-tab">
|
|
436
|
-
<b> Property type: </b>
|
|
437
|
-
{#each allTypes[selectedElemIndex] || [] as type, typeIndex}
|
|
438
|
-
<!-- Only display "custom" on "inline" rule -->
|
|
439
|
-
{#if type !== 'custom' || (currentRule === 'inline' && type === 'custom' && hasDisplayedCustom )}
|
|
440
|
-
<span class:selected="{selectedTypeIndex === typeIndex}" on:click="{() => {selectedTypeIndex = typeIndex;}}"> {type} </span>
|
|
441
|
-
{/if}
|
|
442
|
-
{/each}
|
|
443
|
-
</div>
|
|
444
|
-
{#if allTypes[selectedElemIndex]}
|
|
445
|
-
<div class="editor">
|
|
446
|
-
{#each propsByType as choices}
|
|
447
|
-
{@const selectedName = choices.props[choices.selected]}
|
|
448
|
-
<div class="prop-section">
|
|
449
|
-
{#if choices.props.length > 1}
|
|
450
|
-
<div> <select on:change="{async (e) => {choices.selected = e.target.value; await tick();}}">
|
|
451
|
-
{#each choices.props as propName, i}
|
|
452
|
-
<option selected={i === choices.selected} value="{i}"> {propName} </option>
|
|
453
|
-
{/each}
|
|
454
|
-
</select> </div>
|
|
455
|
-
{:else}
|
|
456
|
-
<span> { selectedName } </span>
|
|
457
|
-
{/if}
|
|
458
|
-
<span class="delete" on:click={() => deleteProp(selectedName)}>✕</span>
|
|
459
|
-
{#if choices.type === 'slider'}
|
|
460
|
-
<input type=range
|
|
461
|
-
min={allCurrentPropDefs[selectedName].min}
|
|
462
|
-
max={allCurrentPropDefs[selectedName].max}
|
|
463
|
-
step={allCurrentPropDefs[selectedName].step || 1}
|
|
464
|
-
value={allCurrentPropDefs[selectedName].value}
|
|
465
|
-
on:change={(e) => updateProp(selectedName, e.target.value, allCurrentPropDefs[selectedName].suffix, e.target)}
|
|
466
|
-
/>
|
|
467
|
-
<span class="current-value"> { allCurrentPropDefs[selectedName].displayed } </span>
|
|
468
|
-
{:else if choices.type == 'select'}
|
|
469
|
-
{@const choices = allCurrentPropDefs[selectedName].choices()}
|
|
470
|
-
<select on:change={(e) => updateProp(selectedName, e.target.value)}>
|
|
471
|
-
{#if !choices.includes(allCurrentPropDefs[selectedName].value)}
|
|
472
|
-
<option selected="true"> --- </option>
|
|
473
|
-
{/if}
|
|
474
|
-
{#each choices as choice}
|
|
475
|
-
<option selected={choice == allCurrentPropDefs[selectedName].value || null}> {choice} </option>
|
|
476
|
-
{/each}
|
|
477
|
-
</select>
|
|
478
|
-
{:else if choices.type == 'color'}
|
|
479
|
-
<ColorPicker
|
|
480
|
-
value={allCurrentPropDefs[selectedName].value}
|
|
481
|
-
onChange={(color => updateProp(selectedName, color))}
|
|
482
|
-
/>
|
|
483
|
-
{/if}
|
|
484
|
-
</div>
|
|
485
|
-
{/each}
|
|
486
|
-
{#if currentRule === 'inline' && bringableToFront[selectedElemIndex] !== null}
|
|
487
|
-
<div class="btn" class:active="{bringableToFront[selectedElemIndex] === true}" on:click="{bringToFront}">
|
|
488
|
-
Bring to front
|
|
489
|
-
</div>
|
|
490
|
-
{/if}
|
|
491
|
-
{#if currentRule === 'inline' && inlineDeletable(currentElement)}
|
|
492
|
-
<div class="btn delete-elem" on:click="{deleteElem}">
|
|
493
|
-
Delete element
|
|
494
|
-
</div>
|
|
495
|
-
{/if}
|
|
496
|
-
</div>
|
|
497
|
-
{/if}
|
|
498
|
-
</div>
|