Commit | Line | Data |
---|---|---|
90b4b964 | 1 | /** |
13744d6e | 2 | * Provides the style editor. |
50aa3a01 | 3 | * |
516c05af AE |
4 | * @author Alexander Ebert |
5 | * @copyright 2001-2019 WoltLab GmbH | |
6 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> | |
90b4b964 | 7 | */ |
adc5c5db | 8 | define(["require", "exports", "tslib", "../../../Ajax", "../../../Core", "../../../Dom/Util", "../../../Event/Handler", "../../../Ui/Screen", "./DarkMode"], function (require, exports, tslib_1, Ajax, Core, Util_1, EventHandler, UiScreen, DarkMode_1) { |
50aa3a01 | 9 | "use strict"; |
516c05af | 10 | Object.defineProperty(exports, "__esModule", { value: true }); |
054537bf AE |
11 | exports.setup = setup; |
12 | exports.hideVisualEditor = hideVisualEditor; | |
13 | exports.showVisualEditor = showVisualEditor; | |
716617cf TD |
14 | Ajax = tslib_1.__importStar(Ajax); |
15 | Core = tslib_1.__importStar(Core); | |
16 | Util_1 = tslib_1.__importDefault(Util_1); | |
17 | EventHandler = tslib_1.__importStar(EventHandler); | |
18 | UiScreen = tslib_1.__importStar(UiScreen); | |
516c05af AE |
19 | const _stylePreviewRegions = new Map(); |
20 | let _stylePreviewRegionMarker; | |
21 | const _stylePreviewWindow = document.getElementById("spWindow"); | |
22 | let _isVisible = true; | |
23 | let _isSmartphone = false; | |
24 | let _updateRegionMarker; | |
50aa3a01 | 25 | /** |
516c05af | 26 | * Handles the switch between static and fluid layout. |
50aa3a01 | 27 | */ |
516c05af AE |
28 | function handleLayoutWidth() { |
29 | const useFluidLayout = document.getElementById("useFluidLayout"); | |
30 | const fluidLayoutMinWidth = document.getElementById("fluidLayoutMinWidth"); | |
31 | const fluidLayoutMaxWidth = document.getElementById("fluidLayoutMaxWidth"); | |
32 | const fixedLayoutVariables = document.getElementById("fixedLayoutVariables"); | |
33 | function change() { | |
34 | if (useFluidLayout.checked) { | |
35 | Util_1.default.show(fluidLayoutMinWidth); | |
36 | Util_1.default.show(fluidLayoutMaxWidth); | |
37 | Util_1.default.hide(fixedLayoutVariables); | |
50aa3a01 | 38 | } |
516c05af AE |
39 | else { |
40 | Util_1.default.hide(fluidLayoutMinWidth); | |
41 | Util_1.default.hide(fluidLayoutMaxWidth); | |
42 | Util_1.default.show(fixedLayoutVariables); | |
50aa3a01 | 43 | } |
516c05af AE |
44 | } |
45 | useFluidLayout.addEventListener("change", change); | |
46 | change(); | |
47 | } | |
48 | /** | |
49 | * Handles SCSS input fields. | |
50 | */ | |
51 | function handleScss(isTainted) { | |
52 | const individualScss = document.getElementById("individualScss"); | |
53 | const overrideScss = document.getElementById("overrideScss"); | |
bdfae1ed AE |
54 | const refreshCodeMirror = (element) => { |
55 | element.codemirror.refresh(); | |
56 | element.codemirror.setCursor(element.codemirror.getCursor()); | |
57 | }; | |
516c05af AE |
58 | if (isTainted) { |
59 | EventHandler.add("com.woltlab.wcf.simpleTabMenu_styleTabMenuContainer", "select", () => { | |
bdfae1ed AE |
60 | refreshCodeMirror(individualScss); |
61 | refreshCodeMirror(overrideScss); | |
516c05af AE |
62 | }); |
63 | } | |
64 | else { | |
65 | EventHandler.add("com.woltlab.wcf.simpleTabMenu_advanced", "select", (data) => { | |
66 | if (data.activeName === "advanced-custom") { | |
bdfae1ed AE |
67 | refreshCodeMirror(document.getElementById("individualScssCustom")); |
68 | refreshCodeMirror(document.getElementById("overrideScssCustom")); | |
516c05af AE |
69 | } |
70 | else if (data.activeName === "advanced-original") { | |
bdfae1ed AE |
71 | refreshCodeMirror(individualScss); |
72 | refreshCodeMirror(overrideScss); | |
516c05af | 73 | } |
50aa3a01 | 74 | }); |
516c05af AE |
75 | } |
76 | } | |
77 | function handleProtection(styleId) { | |
78 | const button = document.getElementById("styleDisableProtectionSubmit"); | |
79 | const checkbox = document.getElementById("styleDisableProtectionConfirm"); | |
80 | checkbox.addEventListener("change", () => { | |
81 | button.disabled = !checkbox.checked; | |
82 | }); | |
83 | button.addEventListener("click", () => { | |
84 | Ajax.apiOnce({ | |
85 | data: { | |
86 | actionName: "markAsTainted", | |
87 | className: "wcf\\data\\style\\StyleAction", | |
88 | objectIDs: [styleId], | |
89 | }, | |
90 | success: () => { | |
91 | window.location.reload(); | |
92 | }, | |
50aa3a01 | 93 | }); |
516c05af AE |
94 | }); |
95 | } | |
1c8485c3 | 96 | function initVisualEditor() { |
516c05af AE |
97 | _stylePreviewWindow.querySelectorAll("[data-region]").forEach((region) => { |
98 | _stylePreviewRegions.set(region.dataset.region, region); | |
99 | }); | |
100 | _stylePreviewRegionMarker = document.createElement("div"); | |
101 | _stylePreviewRegionMarker.id = "stylePreviewRegionMarker"; | |
102 | _stylePreviewRegionMarker.innerHTML = '<div id="stylePreviewRegionMarkerBottom"></div>'; | |
103 | Util_1.default.hide(_stylePreviewRegionMarker); | |
104 | document.getElementById("colors").appendChild(_stylePreviewRegionMarker); | |
105 | const container = document.getElementById("spSidebar"); | |
106 | const select = document.getElementById("spCategories"); | |
107 | let lastValue = select.value; | |
108 | _updateRegionMarker = () => { | |
109 | if (_isSmartphone) { | |
110 | return; | |
111 | } | |
112 | if (lastValue === "none") { | |
113 | Util_1.default.hide(_stylePreviewRegionMarker); | |
114 | return; | |
115 | } | |
116 | const region = _stylePreviewRegions.get(lastValue); | |
117 | const rect = region.getBoundingClientRect(); | |
118 | let top = rect.top + (window.scrollY || window.pageYOffset); | |
119 | Util_1.default.setStyles(_stylePreviewRegionMarker, { | |
120 | height: `${region.clientHeight + 20}px`, | |
121 | left: `${rect.left + document.body.scrollLeft - 10}px`, | |
122 | top: `${top - 10}px`, | |
123 | width: `${region.clientWidth + 20}px`, | |
50aa3a01 | 124 | }); |
516c05af AE |
125 | Util_1.default.show(_stylePreviewRegionMarker); |
126 | top = Util_1.default.offset(region).top; | |
127 | // `+ 80` = account for sticky header + selection markers (20px) | |
128 | const firstVisiblePixel = (window.pageYOffset || window.scrollY) + 80; | |
129 | if (firstVisiblePixel > top) { | |
130 | window.scrollTo(0, Math.max(top - 80, 0)); | |
131 | } | |
132 | else { | |
133 | const lastVisiblePixel = window.innerHeight + (window.pageYOffset || window.scrollY); | |
134 | if (lastVisiblePixel < top) { | |
135 | window.scrollTo(0, top); | |
50aa3a01 TD |
136 | } |
137 | else { | |
516c05af AE |
138 | const bottom = top + region.offsetHeight + 20; |
139 | if (lastVisiblePixel < bottom) { | |
140 | window.scrollBy(0, bottom - top); | |
50aa3a01 TD |
141 | } |
142 | } | |
516c05af AE |
143 | } |
144 | }; | |
516c05af AE |
145 | const callbackChange = () => { |
146 | let element = container.querySelector(`.spSidebarBox[data-category="${lastValue}"]`); | |
147 | Util_1.default.hide(element); | |
148 | lastValue = select.value; | |
149 | element = container.querySelector(`.spSidebarBox[data-category="${lastValue}"]`); | |
150 | Util_1.default.show(element); | |
516c05af AE |
151 | // set region marker |
152 | _updateRegionMarker(); | |
153 | }; | |
154 | select.addEventListener("change", callbackChange); | |
155 | // apply CSS rules | |
156 | const style = document.createElement("style"); | |
157 | style.appendChild(document.createTextNode("")); | |
158 | style.dataset.createdBy = "WoltLab/Acp/Ui/Style/Editor"; | |
159 | document.head.appendChild(style); | |
1c8485c3 | 160 | const spWindow = document.getElementById("spWindow"); |
516c05af AE |
161 | const wrapper = document.getElementById("spVariablesWrapper"); |
162 | wrapper.querySelectorAll(".styleVariableColor").forEach((colorField) => { | |
163 | const variableName = colorField.dataset.store.replace(/_value$/, ""); | |
164 | const observer = new MutationObserver((mutations) => { | |
165 | mutations.forEach((mutation) => { | |
166 | if (mutation.attributeName === "style") { | |
1c8485c3 | 167 | spWindow.style.setProperty(`--${variableName}`, colorField.style.getPropertyValue("background-color")); |
50aa3a01 | 168 | } |
50aa3a01 TD |
169 | }); |
170 | }); | |
516c05af AE |
171 | observer.observe(colorField, { |
172 | attributes: true, | |
173 | }); | |
1c8485c3 | 174 | spWindow.style.setProperty(`--${variableName}`, colorField.style.getPropertyValue("background-color")); |
516c05af AE |
175 | }); |
176 | // category selection by clicking on the area | |
177 | const buttonToggleColorPalette = document.querySelector(".jsButtonToggleColorPalette"); | |
178 | const buttonSelectCategoryByClick = document.querySelector(".jsButtonSelectCategoryByClick"); | |
179 | function toggleSelectionMode() { | |
180 | buttonSelectCategoryByClick.classList.toggle("active"); | |
181 | buttonToggleColorPalette.classList.toggle("disabled"); | |
182 | _stylePreviewWindow.classList.toggle("spShowRegions"); | |
183 | _stylePreviewRegionMarker.classList.toggle("forceHide"); | |
184 | select.disabled = !select.disabled; | |
185 | } | |
186 | buttonSelectCategoryByClick.addEventListener("click", (event) => { | |
187 | event.preventDefault(); | |
188 | toggleSelectionMode(); | |
189 | }); | |
190 | _stylePreviewWindow.querySelectorAll("[data-region]").forEach((region) => { | |
191 | region.addEventListener("click", (event) => { | |
192 | if (!_stylePreviewWindow.classList.contains("spShowRegions")) { | |
193 | return; | |
194 | } | |
50aa3a01 | 195 | event.preventDefault(); |
516c05af AE |
196 | event.stopPropagation(); |
197 | toggleSelectionMode(); | |
198 | select.value = region.dataset.region; | |
199 | // Programmatically trigger the change event handler, rather than dispatching an event, | |
200 | // because Firefox fails to execute the event if it has previously been disabled. | |
201 | // See https://bugzilla.mozilla.org/show_bug.cgi?id=1426856 | |
202 | callbackChange(); | |
50aa3a01 | 203 | }); |
516c05af AE |
204 | }); |
205 | // toggle view | |
206 | const spSelectCategory = document.getElementById("spSelectCategory"); | |
207 | buttonToggleColorPalette.addEventListener("click", (event) => { | |
208 | event.preventDefault(); | |
209 | buttonSelectCategoryByClick.classList.toggle("disabled"); | |
210 | Util_1.default.toggle(spSelectCategory); | |
211 | buttonToggleColorPalette.classList.toggle("active"); | |
212 | _stylePreviewWindow.classList.toggle("spColorPalette"); | |
213 | _stylePreviewRegionMarker.classList.toggle("forceHide"); | |
214 | select.disabled = !select.disabled; | |
215 | }); | |
216 | } | |
217 | /** | |
218 | * Sets up dynamic style options. | |
219 | */ | |
220 | function setup(options) { | |
221 | handleLayoutWidth(); | |
222 | handleScss(options.isTainted); | |
adc5c5db | 223 | (0, DarkMode_1.setup)(); |
516c05af AE |
224 | if (!options.isTainted) { |
225 | handleProtection(options.styleId); | |
226 | } | |
1c8485c3 | 227 | initVisualEditor(); |
516c05af AE |
228 | UiScreen.on("screen-sm-down", { |
229 | match() { | |
230 | hideVisualEditor(); | |
231 | }, | |
232 | unmatch() { | |
233 | showVisualEditor(); | |
234 | }, | |
235 | setup() { | |
236 | hideVisualEditor(); | |
237 | }, | |
238 | }); | |
239 | function callbackRegionMarker() { | |
240 | if (_isVisible) { | |
241 | _updateRegionMarker(); | |
242 | } | |
50aa3a01 | 243 | } |
516c05af AE |
244 | window.addEventListener("resize", callbackRegionMarker); |
245 | EventHandler.add("com.woltlab.wcf.AcpMenu", "resize", callbackRegionMarker); | |
246 | EventHandler.add("com.woltlab.wcf.simpleTabMenu_styleTabMenuContainer", "select", function (data) { | |
247 | _isVisible = data.activeName === "colors"; | |
248 | callbackRegionMarker(); | |
249 | }); | |
250 | } | |
516c05af AE |
251 | function hideVisualEditor() { |
252 | Util_1.default.hide(_stylePreviewWindow); | |
253 | document.getElementById("spVariablesWrapper").style.removeProperty("transform"); | |
254 | Util_1.default.hide(document.getElementById("stylePreviewRegionMarker")); | |
255 | _isSmartphone = true; | |
256 | } | |
516c05af AE |
257 | function showVisualEditor() { |
258 | Util_1.default.show(_stylePreviewWindow); | |
259 | window.setTimeout(() => { | |
260 | Core.triggerEvent(document.getElementById("spCategories"), "change"); | |
261 | }, 100); | |
262 | _isSmartphone = false; | |
263 | } | |
90b4b964 | 264 | }); |