inline-style-editor 1.4.0 → 1.4.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.
@@ -0,0 +1,616 @@
1
+ <script>
2
+ import { onMount, onDestroy, tick } from "svelte";
3
+
4
+ import "../assets/index.scss";
5
+ import { pick, debounce, pascalCaseToSentence, capitalizeFirstLetter, nbChars } from "../util/util";
6
+ import { computeContours } from "../util/boxesContour";
7
+ import ColorPicker from "./ColorPicker.svelte";
8
+ import { getFonts } from "../util/fonts";
9
+
10
+ const strokeElements = [
11
+ "altGlyph",
12
+ "circle",
13
+ "ellipse",
14
+ "line",
15
+ "path",
16
+ "polygon",
17
+ "polyline",
18
+ "rect",
19
+ "text",
20
+ "textPath",
21
+ "tref",
22
+ "tspan",
23
+ ];
24
+
25
+ const borderProps = ["border-radius", "border-width", "border-color", "border-style"];
26
+ const backgroundProps = ["background-color"];
27
+ const fontProps = ["font-family", "font-size", "font-weight", "color"];
28
+ const pathProps = ["stroke-width", "stroke", "stroke-dasharray", "stroke-linejoin", "fill"];
29
+ const cssPropByType = {
30
+ "border-radius": { type: "slider", min: 0, max: 30, suffix: "px" },
31
+ "border-width": { type: "slider", min: 0, max: 30, suffix: "px" },
32
+ "border-style": {
33
+ type: "select",
34
+ choices: () => ["none", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset"],
35
+ },
36
+ "border-color": { type: "color" },
37
+ "font-family": { type: "select", choices: getFontFamilies },
38
+ "font-size": { type: "slider", min: 0, max: 40, suffix: "px" },
39
+ "font-weight": { type: "slider", min: 0, max: 800 },
40
+ color: { type: "color" },
41
+ "stroke-width": {
42
+ type: "slider",
43
+ min: 0,
44
+ max: 20,
45
+ step: 0.5,
46
+ suffix: "px",
47
+ },
48
+ stroke: { type: "color" },
49
+ "stroke-linejoin": {
50
+ type: "select",
51
+ choices: () => ["bevel", "miter", "round"],
52
+ },
53
+ fill: { type: "color" },
54
+ "stroke-dasharray": { type: "slider", min: 0, max: 30, suffix: "px" },
55
+ "background-color": { type: "color" },
56
+ };
57
+
58
+ // Props
59
+ const props = $props();
60
+ const getElems = props.getElems ?? null;
61
+ const listenOnClick = props.listenOnClick ?? false;
62
+ const onStyleChanged = props.onStyleChanged ?? (() => {});
63
+ const customProps = props.customProps ?? {};
64
+ const inlineDeletable = props.inlineDeletable ?? (() => true);
65
+ const cssRuleFilter = props.cssRuleFilter ?? null;
66
+ const getCssRuleName =
67
+ props.getCssRuleName ??
68
+ ((cssRuleName, element) => {
69
+ if (cssRuleName === "inline") return "Selected element";
70
+ return cssRuleName;
71
+ });
72
+
73
+ const typeText = "text";
74
+ const typeBorder = "border";
75
+ const typeStroke = "stroke";
76
+ const typeBackground = "background";
77
+ const customType = "custom";
78
+ const propByType = {
79
+ [typeText]: fontProps,
80
+ [typeBorder]: borderProps,
81
+ [typeStroke]: pathProps,
82
+ [typeBackground]: backgroundProps,
83
+ [customType]: Object.keys(customProps),
84
+ };
85
+ const inputTypeOrder = { slider: 0, select: 1, color: 2 };
86
+
87
+ // State variables
88
+ let elementToListen = $state(null);
89
+ let clickedElement = $state(null);
90
+ let positionAnchor = $state();
91
+ let self = $state();
92
+ let helperElemWrapper = $state();
93
+ let pathWithHoles = $state("");
94
+ let pageDimensions = $state({ width: 0, height: 0 });
95
+ let targetsToSearch = $state([[]]);
96
+ let allRules = $state([]); // list of list of CSS rules, for every target element
97
+ let allTypes = $state([]); // list of list of types (e.g color, border), for every target element
98
+ let selectedElemIndex = $state(0);
99
+ let selectedRuleIndex = $state(0);
100
+ let selectedTypeIndex = $state(0);
101
+ let propsByType = $state(); // propType -> {[props], selected}
102
+ let allCurrentPropDefs = $state({}); // propName => selectorDef
103
+ let bringableToFront = $state([]); // null = not bringable, true = bringable, false = was bringed
104
+ let hasDisplayedCustom = $state(false);
105
+
106
+ // Reactive derived values
107
+ const currentElement = $derived(targetsToSearch[selectedElemIndex]?.[0]);
108
+ const currentRule = $derived(allRules[selectedElemIndex]?.[selectedRuleIndex]);
109
+ let curType = $state();
110
+
111
+ // Effects
112
+ $effect(() => {
113
+ if (elementToListen !== null) {
114
+ init();
115
+ }
116
+ });
117
+
118
+ $effect(() => {
119
+ if (allTypes[selectedElemIndex]?.[selectedTypeIndex] !== curType) {
120
+ curType = allTypes[selectedElemIndex]?.[selectedTypeIndex];
121
+ }
122
+ });
123
+
124
+ $effect(() => {
125
+ if (curType || selectedRuleIndex || selectedElemIndex) {
126
+ initAndGroup();
127
+ }
128
+ });
129
+
130
+ onMount(() => {
131
+ close();
132
+ elementToListen = self.parentNode;
133
+ document.body.appendChild(self);
134
+ document.body.appendChild(helperElemWrapper);
135
+ document.body.appendChild(positionAnchor);
136
+ udpatePageDimensions();
137
+ // make sure the layout is computed to get the client size
138
+ setTimeout(() => {
139
+ udpatePageDimensions();
140
+ }, 1000);
141
+ window.addEventListener("resize", udpatePageDimensions);
142
+ });
143
+
144
+ onDestroy(() => {
145
+ window.removeEventListener("resize", udpatePageDimensions);
146
+ if (listenOnClick) elementToListen.removeEventListener("click", _open);
147
+ });
148
+
149
+ function getFontFamilies() {
150
+ return getFonts();
151
+ }
152
+
153
+ function initAndGroup() {
154
+ const allProps = { ...cssPropByType, ...customProps };
155
+ const _allCurrentPropDefs = pick(allProps, propByType[curType]);
156
+ Object.keys(_allCurrentPropDefs).forEach((key) => {
157
+ const propSelectType = _allCurrentPropDefs[key].type;
158
+ let retrieveType = "number";
159
+ if (propSelectType === "color") retrieveType = "rgb";
160
+ else if (propSelectType === "select") retrieveType = "raw";
161
+ if (_allCurrentPropDefs[key].getter) {
162
+ const val = _allCurrentPropDefs[key].getter(currentElement);
163
+ if (val === null) {
164
+ delete _allCurrentPropDefs[key];
165
+ return;
166
+ }
167
+ _allCurrentPropDefs[key].value = val;
168
+ _allCurrentPropDefs[key].displayed = val;
169
+ } else {
170
+ _allCurrentPropDefs[key].displayed = getComputedPropValue(currentElement, key, "raw");
171
+ _allCurrentPropDefs[key].value = getComputedPropValue(currentElement, key, retrieveType);
172
+ }
173
+ });
174
+
175
+ propsByType = Object.entries(_allCurrentPropDefs)
176
+ .reduce((byType, [propName, selectorDef]) => {
177
+ const selectorType = selectorDef.type;
178
+ const existing = byType.find((x) => x.type === selectorType);
179
+ if (!existing)
180
+ byType.push({
181
+ selected: 0,
182
+ props: [propName],
183
+ type: selectorType,
184
+ });
185
+ else existing.props.push(propName);
186
+ return byType;
187
+ }, [])
188
+ .sort((a, b) => {
189
+ if (inputTypeOrder[a.type] < inputTypeOrder[b.type]) return -1;
190
+ if (inputTypeOrder[a.type] > inputTypeOrder[b.type]) return 1;
191
+ return 0;
192
+ });
193
+ allCurrentPropDefs = _allCurrentPropDefs;
194
+ updateHelpers();
195
+ }
196
+
197
+ function getRuleNames(rules) {
198
+ if (!rules) return [];
199
+ return rules.map((rule, i) => {
200
+ if (rule === "inline") return "inline";
201
+ const cssSelector = rule.selectorText;
202
+ const title = rule.parentStyleSheet.title || `${i}`;
203
+ return `${title}: ${cssSelector}`;
204
+ });
205
+ }
206
+
207
+ function getRuleNamesTransformed(rules) {
208
+ return getRuleNames(rules).map((name) => getCssRuleName(name, clickedElement));
209
+ }
210
+
211
+ let warningDisplayed = new Set();
212
+ function getMatchedCSSRules(elems) {
213
+ const sheets = document.styleSheets;
214
+ return elems.reduce((matchedRulesByElem, elemDef) => {
215
+ const el = elemDef[0];
216
+ const matchedRules = ["inline"];
217
+ for (let i in sheets) {
218
+ try {
219
+ const rules = sheets[i].cssRules;
220
+ for (let r in rules) {
221
+ let selectorText = rules[r].selectorText;
222
+ if (!selectorText || rules[r].selectorText.length > 50) continue; // skip selectors too long
223
+ if (selectorText.split(",").some((selector) => selector === "*")) continue; // skip * selector
224
+ if (selectorText.endsWith(":hover"))
225
+ selectorText = selectorText.substring(0, selectorText.length - ":hover".length);
226
+ if (el.matches(selectorText)) {
227
+ if (cssRuleFilter !== null && !cssRuleFilter(el, rules[r].selectorText)) continue;
228
+ matchedRules.push(rules[r]);
229
+ }
230
+ }
231
+ } catch (err) {
232
+ if (!warningDisplayed.has(i)) {
233
+ console.warn(
234
+ "Style editor: Not able to access",
235
+ sheets[i].ownerNode,
236
+ "sheet. Try CORS loading the sheet if you want to edit it.",
237
+ );
238
+ warningDisplayed.add(i);
239
+ }
240
+ }
241
+ }
242
+ matchedRulesByElem.push(matchedRules);
243
+ return matchedRulesByElem;
244
+ }, []);
245
+ }
246
+
247
+ function getEditableTypes(elems) {
248
+ return elems.reduce((typesByElem, elemDef) => {
249
+ const elem = elemDef[0];
250
+ const types = [];
251
+ if (elem.firstChild && (elem.firstChild.nodeType === 3 || elem.firstChild.tagName === "tspan")) {
252
+ // Node.TEXT_NODE
253
+ types.push(typeText);
254
+ }
255
+ const elemTagName = elem.tagName.toLowerCase();
256
+ let bringable = false;
257
+ if (strokeElements.includes(elemTagName)) {
258
+ types.push(typeStroke);
259
+ const parentTag = elem.parentElement.tagName.toLowerCase();
260
+ if (
261
+ parentTag === "g" &&
262
+ elem.previousElementSibling &&
263
+ elem.previousElementSibling.tagName.toLowerCase() == elemTagName
264
+ ) {
265
+ bringable = true;
266
+ }
267
+ } else {
268
+ types.push(typeBorder);
269
+ types.push(typeBackground);
270
+ }
271
+ if (bringable) bringableToFront.push(true);
272
+ else bringableToFront.push(null);
273
+ typesByElem.push(types);
274
+ return typesByElem;
275
+ }, []);
276
+ }
277
+
278
+ function init() {
279
+ if (listenOnClick) elementToListen.addEventListener("click", _open);
280
+ }
281
+
282
+ function _open(e) {
283
+ open(e.target, e.pageX, e.pageY);
284
+ }
285
+
286
+ export async function open(el, x, y) {
287
+ clickedElement = el;
288
+ udpatePageDimensions();
289
+ if (el.classList.contains("overlay-over")) return overlayClicked();
290
+ else if (self.contains(el)) return;
291
+ selectedElemIndex = 0;
292
+ selectedRuleIndex = 0;
293
+ selectedTypeIndex = 0;
294
+ bringableToFront = [];
295
+ allTypes = [];
296
+ allRules = [];
297
+ if (getElems) targetsToSearch = getElems(el);
298
+ else targetsToSearch = [[el, "Clicked"]];
299
+ allTypes = getEditableTypes(targetsToSearch);
300
+ hasDisplayedCustom = false;
301
+ allRules = getMatchedCSSRules(targetsToSearch);
302
+ for (let def of Object.values(customProps)) {
303
+ if (def.getter(el) !== null) {
304
+ hasDisplayedCustom = true;
305
+ break;
306
+ }
307
+ }
308
+ if (Object.keys(customProps).length) {
309
+ allTypes[0].push(customType);
310
+ }
311
+ await tick();
312
+ initAndGroup();
313
+ if (x && y) show(x, y);
314
+ else {
315
+ const rect = getBoundingBoxInfos(el, 15);
316
+ show(rect.left, rect.top);
317
+ }
318
+ }
319
+
320
+ export function close() {
321
+ self.style.display = "none";
322
+ helperElemWrapper.style.display = "none";
323
+ pathWithHoles = "";
324
+ }
325
+
326
+ export function isOpened() {
327
+ return self.style.display === "block";
328
+ }
329
+
330
+ function overlayClicked() {
331
+ close();
332
+ }
333
+
334
+ function show(x, y) {
335
+ self.style.display = "block";
336
+ self.style.opacity = 0;
337
+ const popupDimension = self.getBoundingClientRect();
338
+ x = x + popupDimension.width + 20 > pageDimensions.width ? x - popupDimension.width - 20 : x + 20;
339
+ y = y + popupDimension.height + 20 > pageDimensions.height ? y - popupDimension.height - 20 : y + 20;
340
+ y = Math.max(y, 0);
341
+ self.style.left = x + "px";
342
+ self.style.top = y + "px";
343
+ helperElemWrapper.style.display = "block";
344
+ self.style.opacity = 1;
345
+ updateHelpers();
346
+ }
347
+
348
+ async function updateHelpers() {
349
+ await tick();
350
+ if (!currentRule) return;
351
+ let matching;
352
+ if (currentRule === "inline") matching = [currentElement];
353
+ else {
354
+ const selector = currentRule.selectorText.replace(/(:hover)|:focus/g, "");
355
+ matching = Array.from(document.querySelectorAll(selector));
356
+ }
357
+ const boundingBoxes = matching.map((el) => getBoundingBoxInfos(el, 10));
358
+ pathWithHoles = computeContours(boundingBoxes, pageDimensions);
359
+ }
360
+
361
+ function getBoundingBoxInfos(el, padding = 0) {
362
+ const rect = el.getBoundingClientRect();
363
+ return {
364
+ left: rect.left + window.scrollX - padding,
365
+ top: rect.top + window.scrollY - padding,
366
+ width: rect.width + padding * 2,
367
+ height: rect.height + padding * 2,
368
+ right: rect.left + window.scrollX + rect.width + padding,
369
+ bottom: rect.top + window.scrollY + rect.height + padding,
370
+ };
371
+ }
372
+
373
+ function _updateProp(propName, val, suffix) {
374
+ const finalValue = suffix ? val + suffix : val;
375
+ if (currentRule === "inline") {
376
+ if (allCurrentPropDefs[propName].setter) {
377
+ allCurrentPropDefs[propName].setter(currentElement, val);
378
+ } else {
379
+ const style = currentElement.style; // do not trigger reactivity on currentElement
380
+ style[propName] = finalValue;
381
+ }
382
+ } else currentRule.style.setProperty(propName, finalValue);
383
+ allCurrentPropDefs[propName].value = val;
384
+ allCurrentPropDefs[propName].displayed = finalValue;
385
+
386
+ onStyleChanged(currentElement, currentRule, propName, finalValue);
387
+ updateHelpers();
388
+ }
389
+ const updateProp = debounce(_updateProp, 100);
390
+
391
+ function udpatePageDimensions() {
392
+ const bodyStyle = getComputedStyle(document.body);
393
+ const marginLeft = parseInt(bodyStyle.marginLeft);
394
+ const marginRight = parseInt(bodyStyle.marginRight);
395
+ const marginTop = parseInt(bodyStyle.marginTop);
396
+ const marginBottom = parseInt(bodyStyle.marginBottom);
397
+ pageDimensions = {
398
+ width: document.body.offsetWidth + marginLeft + marginRight,
399
+ height: document.body.offsetHeight + marginTop + marginBottom,
400
+ };
401
+ }
402
+
403
+ function cssRgbToHex(rgbStr) {
404
+ const m = rgbStr.match(/[0-9\.]+/g).map((i) => parseFloat(i));
405
+ if (m.length === 3) m.push(1);
406
+ return m.reduce((hexStr, cur, i) => {
407
+ if (i === 3)
408
+ hexStr += Math.round(cur * 255)
409
+ .toString(16)
410
+ .padStart(2, "0");
411
+ else hexStr += cur.toString(16).padStart(2, "0");
412
+ return hexStr;
413
+ }, "#");
414
+ }
415
+
416
+ // type one of: "number", "rgb", "font", "raw"
417
+ function parsePropvalue(value, type = "number") {
418
+ if (type == "raw") return value;
419
+ if (type == "number" && /[0-9]+(px)|(em)|(rem)/.test(value)) return parseInt(value);
420
+ if (type == "rgb") {
421
+ if (value === "none") return "#00000000";
422
+ if (value.includes("rgb") || value[0] == "#") return cssRgbToHex(value);
423
+ }
424
+ return value;
425
+ }
426
+
427
+ function getComputedPropValue(el, cssProp, type = "number") {
428
+ let currentStyleValue = currentRule?.style?.[cssProp];
429
+ if (!currentStyleValue) {
430
+ const computed = getComputedStyle(el);
431
+ currentStyleValue = computed[cssProp];
432
+ }
433
+ return parsePropvalue(currentStyleValue, type);
434
+ }
435
+
436
+ function bringToFront() {
437
+ bringableToFront[selectedElemIndex] = false;
438
+ currentElement.parentNode.appendChild(currentElement);
439
+ onStyleChanged(currentElement, currentRule, "bringtofront", null);
440
+ }
441
+
442
+ function deleteElem() {
443
+ currentElement.remove();
444
+ close();
445
+ }
446
+ function deleteProp(propName) {
447
+ if (currentRule === "inline") {
448
+ currentElement.style.removeProperty(propName);
449
+ } else {
450
+ currentRule.style.removeProperty(propName);
451
+ }
452
+ onStyleChanged(currentElement, currentRule, propName, null);
453
+ initAndGroup();
454
+ }
455
+
456
+ function selectRule(ruleIndex) {
457
+ const newRule = allRules[selectedElemIndex]?.[ruleIndex];
458
+ if (newRule !== "inline" && selectedTypeIndex === allTypes[selectedElemIndex].length - 1) {
459
+ selectedTypeIndex = 0;
460
+ }
461
+ selectedRuleIndex = ruleIndex;
462
+ }
463
+ </script>
464
+
465
+ <div bind:this={positionAnchor} style="position: absolute;"></div>
466
+ <svg
467
+ bind:this={helperElemWrapper}
468
+ class="ise-helper-wrapper"
469
+ version="1.1"
470
+ xmlns="http://www.w3.org/2000/svg"
471
+ xmlns:xlink="http://www.w3.org/1999/xlink"
472
+ width={pageDimensions.width}
473
+ height={pageDimensions.height}
474
+ onclick={overlayClicked}
475
+ >
476
+ <clipPath id="overlay-clip" clip-rule="evenodd">
477
+ <path d={pathWithHoles} />
478
+ </clipPath>
479
+ <rect y="0" x="0" height="100%" width="100%" class="overlay-over" />
480
+ </svg>
481
+
482
+ <div class="ise" bind:this={self}>
483
+ <div class="close-button" onclick={close}>x</div>
484
+ {#if targetsToSearch.length > 1}
485
+ <div class="select-tab">
486
+ <b> Element </b>
487
+ {#each targetsToSearch as [_, name], elemIndex}
488
+ <span
489
+ class:selected={selectedElemIndex === elemIndex}
490
+ onclick={() => {
491
+ selectedElemIndex = elemIndex;
492
+ selectedRuleIndex = 0;
493
+ }}
494
+ >
495
+ {name}
496
+ </span>
497
+ {/each}
498
+ </div>
499
+ {/if}
500
+ <div class="select-tab">
501
+ <b> Applied to: </b>
502
+ {#if nbChars(getRuleNamesTransformed(allRules[selectedElemIndex])) > 30}
503
+ <select onchange={(e) => selectRule(e.target.value)}>
504
+ {#each getRuleNames(allRules[selectedElemIndex]) as ruleName, ruleIndex}
505
+ <option selected={selectedRuleIndex === ruleIndex} value={ruleIndex}
506
+ >{getCssRuleName(ruleName, clickedElement)}</option
507
+ >
508
+ {/each}
509
+ </select>
510
+ {:else}
511
+ {#each getRuleNames(allRules[selectedElemIndex]) as ruleName, ruleIndex}
512
+ <span
513
+ title={ruleName}
514
+ class:selected={selectedRuleIndex === ruleIndex}
515
+ onclick={() => {
516
+ selectRule(ruleIndex);
517
+ }}
518
+ >
519
+ {getCssRuleName(ruleName, clickedElement)}</span
520
+ >
521
+ {/each}
522
+ {/if}
523
+ </div>
524
+ <div class="select-tab">
525
+ <b> Property type: </b>
526
+ {#each allTypes[selectedElemIndex] || [] as type, typeIndex}
527
+ <!-- Only display "custom" on "inline" rule -->
528
+ {#if type !== "custom" || (currentRule === "inline" && type === "custom" && hasDisplayedCustom)}
529
+ <span
530
+ class:selected={selectedTypeIndex === typeIndex}
531
+ onclick={() => {
532
+ selectedTypeIndex = typeIndex;
533
+ }}
534
+ >
535
+ {type === "stroke" ? "SVG paint" : capitalizeFirstLetter(type)}
536
+ </span>
537
+ {/if}
538
+ {/each}
539
+ </div>
540
+ {#if allTypes[selectedElemIndex]}
541
+ <div class="editor">
542
+ {#each propsByType as choices}
543
+ {@const selectedName = choices.props[choices.selected]}
544
+ <div class="prop-section {choices.type}">
545
+ <div class="prop-name">
546
+ {#if choices.props.length > 1}
547
+ <select
548
+ onchange={async (e) => {
549
+ choices.selected = e.target.value;
550
+ await tick();
551
+ }}
552
+ >
553
+ {#each choices.props as propName, i}
554
+ <option selected={i === choices.selected} value={i}>
555
+ {#if choices.type === "color"}
556
+ {capitalizeFirstLetter(propName)} color
557
+ {:else}
558
+ {pascalCaseToSentence(propName)}
559
+ {/if}
560
+ </option>
561
+ {/each}
562
+ </select>
563
+ {:else}
564
+ <span> {pascalCaseToSentence(selectedName)} </span>
565
+ {/if}
566
+ <span class="delete" onclick={() => deleteProp(selectedName)}>✕</span>
567
+ </div>
568
+ {#if choices.type === "slider"}
569
+ <input
570
+ type="range"
571
+ min={allCurrentPropDefs[selectedName].min}
572
+ max={allCurrentPropDefs[selectedName].max}
573
+ step={allCurrentPropDefs[selectedName].step || 1}
574
+ value={allCurrentPropDefs[selectedName].value}
575
+ onchange={(e) =>
576
+ updateProp(
577
+ selectedName,
578
+ e.target.value,
579
+ allCurrentPropDefs[selectedName].suffix,
580
+ e.target,
581
+ )}
582
+ />
583
+ <span class="current-value">
584
+ {allCurrentPropDefs[selectedName].displayed}
585
+ </span>
586
+ {:else if choices.type == "select"}
587
+ {@const choices = allCurrentPropDefs[selectedName].choices()}
588
+ <select onchange={(e) => updateProp(selectedName, e.target.value)}>
589
+ {#if !choices.includes(allCurrentPropDefs[selectedName].value)}
590
+ <option selected="true"> --- </option>
591
+ {/if}
592
+ {#each choices as choice}
593
+ <option selected={choice == allCurrentPropDefs[selectedName].value || null}>
594
+ {choice}
595
+ </option>
596
+ {/each}
597
+ </select>
598
+ {:else if choices.type == "color"}
599
+ <ColorPicker
600
+ value={allCurrentPropDefs[selectedName].value}
601
+ onChange={(color) => updateProp(selectedName, color)}
602
+ />
603
+ {/if}
604
+ </div>
605
+ {/each}
606
+ {#if currentRule === "inline" && bringableToFront[selectedElemIndex] !== null}
607
+ <div class="btn" class:active={bringableToFront[selectedElemIndex] === true} onclick={bringToFront}>
608
+ Bring to front
609
+ </div>
610
+ {/if}
611
+ {#if currentRule === "inline" && inlineDeletable(currentElement)}
612
+ <div class="btn delete-elem" onclick={deleteElem}>Delete element</div>
613
+ {/if}
614
+ </div>
615
+ {/if}
616
+ </div>
package/src/index.js ADDED
@@ -0,0 +1,8 @@
1
+ import { mount } from 'svelte';
2
+ import StyleEditor from './components/InlineStyleEditor.svelte';
3
+ export default class InlineStyleEditor {
4
+ constructor(options) {
5
+ const { target = document.body, ...props } = options;
6
+ return mount(StyleEditor, { target, props });
7
+ }
8
+ }