Last active
October 17, 2022 09:07
-
-
Save uxdxdev/467336d64f1a130ef622499634dd098c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name SPM Custom CSS Properties | |
// @namespace http://tampermonkey.net/ | |
// @version 0.35 | |
// @description SPM UI theme creation using CSS properties | |
// @author https://github.com/daithimorton | |
// @match https://*/* | |
// @match http://*/* | |
// @grant none | |
// @downloadUrl https://gist.github.com/daithimorton/467336d64f1a130ef622499634dd098c/raw/spmcssvar.user.js | |
// @updateUrl https://gist.github.com/daithimorton/467336d64f1a130ef622499634dd098c/raw/spmcssvar.user.js | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
// functions | |
function isCuramApp() { | |
return document.getElementsByTagName('body')[0].classList.contains('curam') | |
} | |
// only run in Curam apps | |
if(!isCuramApp()){ | |
return; | |
} | |
function ready(fn) { | |
if (document.readyState != 'loading'){ | |
fn(); | |
} else { | |
document.addEventListener('DOMContentLoaded', fn); | |
} | |
} | |
function inRootWindow() { | |
try { | |
return window.self === window.top; | |
} catch (e) { | |
return true; | |
} | |
} | |
function toggleSidebar() { | |
const sidebarPanel = document.getElementsByClassName("sidebar__panel")[0]; | |
if(sidebarPanel.classList.contains('hide')){ | |
sidebarPanel.classList.remove("hide"); | |
} else { | |
sidebarPanel.classList.add("hide"); | |
} | |
} | |
function setPx(value) { | |
return value + 'px'; | |
} | |
function calculateLeft(boundingBox){ | |
let left = 0; | |
if((boundingBox.x + boundingBox.width) < (window.innerWidth / 2)){ | |
left = window.innerWidth / 2; | |
} | |
return setPx(left); | |
} | |
function positionTooltip(element) { | |
const boundingBox = element.getBoundingClientRect(); | |
window.top.tooltip.style.left = calculateLeft(boundingBox); | |
window.top.tooltip.style.top = 0; | |
} | |
function addBorderToElement(el) { | |
let element = el; | |
var sheets = document.styleSheets; | |
if(element){ | |
element.matches = element.matches || element.webkitMatchesSelector || element.mozMatchesSelector || element.msMatchesSelector || element.oMatchesSelector; | |
const data = []; | |
for (var i in sheets) { | |
var rules = sheets[i].rules || sheets[i].cssRules; | |
for (var r in rules) { | |
if (rules[r].selectorText && element.matches(rules[r].selectorText) && rules[r].style.cssText.includes('var(--')) { | |
data.push({ | |
selector: rules[r].selectorText, | |
props: rules[r].style.cssText.split(";").filter(item => item.includes('var(--')) | |
}) | |
element.style.cssText += 'border: 2px solid red !important;' | |
if(!window.top.tooltip.classList.contains('showtooltip')){ | |
window.top.tooltip.classList.add('showtooltip'); | |
} | |
positionTooltip(element) | |
} | |
} | |
} | |
window.top.tooltip.innerHTML = ``; | |
data.forEach((item, index) => { | |
const selectorText = document.createElement('p'); | |
selectorText.classList.add('font-bold') | |
selectorText.innerHTML = item.selector; | |
window.top.tooltip.append(selectorText); | |
item.props && item.props.forEach(propData =>{ | |
const propText = document.createElement('p'); | |
propText.innerHTML = propData; | |
window.top.tooltip.append(propText); | |
}) | |
if(index !== (data.length -1)){ | |
const lineBreak = document.createElement('br'); | |
window.top.tooltip.append(lineBreak); | |
} | |
}) | |
} | |
} | |
function removeBorderFromElement(element) { | |
const regex = /border:.*!important;/ | |
const newStyle = element.style.cssText.replace(regex, ''); | |
element.style.cssText = newStyle; | |
// hide tooltip | |
window.top.tooltip.classList.remove(`showtooltip`) | |
} | |
const defaultRootCssOverrides = `:root { | |
} | |
`; | |
const defaultOtherCSSOverrides = ` | |
.infotooltip { | |
position: fixed; | |
z-index: 1000; | |
display: block; | |
background: #ffffff; | |
padding: 1rem; | |
font-size: 1rem; | |
border: 1px solid #161616; | |
max-width: 50vw; | |
color: #161616; | |
} | |
.hidetooltip { | |
visibility: hidden; | |
} | |
.showtooltip { | |
visibility: visible; | |
} | |
.font-bold { | |
font-weight: 700; | |
} | |
`; | |
// add a '\' to escape any regex special characters before using this string in regex. | |
// e.g. if there is a '+' character in your string it will now become \+ so that regex does not | |
// interperet it. | |
function escapeRegExp(text) { | |
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); | |
} | |
const highlightElementsUsingSelector = (selector) =>{ | |
for(let iframeStyleIndex = 0; iframeStyleIndex < window.top.iframeStyles.length; iframeStyleIndex++){ | |
const found = window.top.iframeStyles[iframeStyleIndex].innerHTML.includes(`\n${selector} {`); | |
if(found){ | |
var regex = new RegExp("^" + escapeRegExp(selector) + " {.*\n?", "m"); | |
const newStyle = window.top.iframeStyles[iframeStyleIndex].innerHTML.replace(regex, ''); | |
window.top.iframeStyles[iframeStyleIndex].innerHTML = newStyle; | |
} else { | |
window.top.iframeStyles[iframeStyleIndex].innerHTML += `${selector} { border: 2px solid red !important; } \n`; | |
} | |
} | |
} | |
// search all stylesheets for properties using CSS variables | |
const getCssPropVarMap = () => { | |
// TODO: optimize this section, its taking too long | |
console.time('-> -> SPM: propVarMap'); | |
const propVarMap = [...document.styleSheets] | |
.reduce((finalArr, sheet) => finalArr.concat([...sheet.cssRules] | |
// we only want to parse type 1 rules https://developer.mozilla.org/en-US/docs/Web/API/CSSRule | |
.filter(rule => rule.type === 1) | |
.reduce((propValArr, rule) => { | |
// for each rule get the property name and its value | |
// TODO: fix bug in Firefox related to Object.keys(rule.style), can't convert to array of keys | |
const props = Object.keys(rule.style) | |
// we only want values using variables, e.g. var() | |
.filter(propName => { | |
return rule.style[propName].includes("var(--") | |
}) | |
.map((propName) => { | |
return { | |
selector: rule.selectorText, | |
props: rule.style.cssText.split(";").filter(item => item.includes('var(--')) | |
} | |
}) | |
return [...propValArr, ...props]; | |
},[])),[]); | |
console.timeEnd('-> -> SPM: propVarMap'); | |
console.time('-> -> SPM: filteredPropVarMap'); | |
const filteredPropVarMap = propVarMap.reduce((finalArray, current) => { | |
const {selector, props} = current; | |
const foundDuplicate = finalArray.find(item => { | |
const dup = item.selector === selector; | |
return dup; | |
}); | |
// check if already added to UI list | |
const isInPropVarMap = window.top.propVarMap && window.top.propVarMap.find(item => { | |
const dup = item.selector === selector; | |
return dup; | |
}); | |
if (!foundDuplicate && !isInPropVarMap) { | |
window.top.propVarMap.push(current); | |
return finalArray.concat([current]); | |
} else { | |
return finalArray; | |
} | |
}, []); | |
console.timeEnd('-> -> SPM: filteredPropVarMap'); | |
return filteredPropVarMap; | |
} | |
const updateSelectorCssPropList = () =>{ | |
const sortBySelectorName = (a, b)=>{ | |
const aPropName = a.selector; | |
const bPropName = b.selector; | |
if (aPropName < bPropName) return -1; | |
if (aPropName > bPropName) return 1; | |
return 0; | |
} | |
console.time('-> SPM: getCssPropVarMap()'); | |
const result = getCssPropVarMap().sort(sortBySelectorName); | |
console.timeEnd('-> SPM: getCssPropVarMap()'); | |
// display the exposed CSS variables in a list on the sidebar | |
for (let i = 0; i < result.length; i++) { | |
const { selector, props } = result[i]; | |
const cssVarListItem = document.createElement('li'); | |
cssVarListItem.classList.add('mb-4'); | |
const selectorName = document.createElement('p'); | |
selectorName.classList.add('font-bold', 'mt-0', 'mb-0'); | |
selectorName.innerHTML = selector; | |
selectorName.classList.add('selector__name'); | |
cssVarListItem.append(selectorName); | |
props.forEach(item => { | |
const propValue = document.createElement('p'); | |
propValue.classList.add('mt-0', 'mb-0'); | |
propValue.innerHTML = item; | |
cssVarListItem.append(propValue); | |
}) | |
const highlightElementButton = document.createElement('button'); | |
highlightElementButton.innerHTML = 'Highlight off' | |
highlightElementButton.classList.add('sidebar__highlight-element-button', 'mb-4', 'mt-2', 'leading-normal') | |
highlightElementButton.onclick = function(){ | |
highlightElementsUsingSelector(selector) | |
// toggle on/off | |
if(highlightElementButton.classList.contains("on")){ | |
highlightElementButton.classList.remove("on"); | |
highlightElementButton.innerHTML = 'Highlight off' | |
}else{ | |
highlightElementButton.classList.add("on"); | |
highlightElementButton.innerHTML = 'Highlight on' | |
} | |
} | |
cssVarListItem.append(highlightElementButton) | |
window.top.cssPropVarList.append(cssVarListItem) | |
if(i === (result.length - 1)){ | |
const hr = document.createElement('hr'); | |
window.top.cssPropVarList.append(hr) | |
} | |
} | |
} | |
const updateExistingRootCssPropsList = () =>{ | |
let rootVariables = [...document.styleSheets].map(styleSheet => [...styleSheet.cssRules].filter(rule => rule.type === 1 && rule.selectorText === ':root')).flat() | |
// rootVariables[0] are the existing CSS properties | |
// rootVariables[1] are the CSS properties defined in the above "defaultRootCssOverrides" variable | |
// e.g. if a customer creates a custom.css file with properties defined in :root | |
// then they will show up in the sidebar UI to highlight that some cusomisations are already | |
// being made. | |
const originalRootCssVariables = rootVariables[0]; | |
// create list of all properties | |
const originalRootCssVariablesStyle = originalRootCssVariables.style; | |
const rootCssNameValueArr = Object.keys(originalRootCssVariablesStyle).map(key=>{ | |
return { | |
name: key, | |
value: originalRootCssVariablesStyle.getPropertyValue(key) | |
} | |
}) | |
for (let i = 0; i < originalRootCssVariablesStyle.length; i++) { | |
const propertyName = originalRootCssVariablesStyle.item(i); | |
const propertyValue = originalRootCssVariablesStyle[propertyName]; | |
const cssRootVarListItem = document.createElement('li'); | |
const variableNameSpan = document.createElement('span'); | |
variableNameSpan.innerHTML = propertyName + ': ' | |
const variableValueSpan = document.createElement('span'); | |
variableValueSpan.classList.add('font-bold') | |
variableValueSpan.innerHTML = propertyValue + ';' | |
cssRootVarListItem.append(variableNameSpan) | |
cssRootVarListItem.append(variableValueSpan) | |
window.top.cssRootVarList.append(cssRootVarListItem) | |
if(i === (originalRootCssVariablesStyle.length - 1)){ | |
const hr = document.createElement('hr'); | |
window.top.cssRootVarList.append(hr) | |
} | |
} | |
} | |
const getCustomCssProps = () => { | |
// get all CSS variables used in all stylesheets | |
const allCssVariableRules = [...document.styleSheets].map(styleSheet => [...styleSheet.cssRules]).flat().filter(rule => rule.type === 1 && rule.cssText.includes('var(--')) | |
// tokenise the rule string that contains both the selector and stringified CSS styling | |
// we want the variable name and its fallback value | |
// we can't get the computed value for this variable here because | |
// we would need to process each element indiviually to compute it's styling | |
const allCssVariableMap = allCssVariableRules.map(rule => { | |
// console.log('spm', rule.style.cssText.split(';').filter(item => item.includes('var(--'))); | |
// match the contents of the highest level var(<contents>) | |
// in some cases there will be nested var() but we only want | |
// the highest level one. | |
const regex = /var\((.*?)\)/; | |
const tokens = rule.cssText.match(regex); | |
const varStr = tokens && tokens[1]; | |
// the variable name is the first item in the match array | |
const variableName = varStr.split(/,(.+)/)[0] | |
// the variable value is the second item in the match array | |
let defaultVariableValue = varStr.split(/,(.+)/)[1]; | |
// if there is a bunch of nested var() then just append a ')' to the end and display | |
if(defaultVariableValue && defaultVariableValue.includes('var(')) defaultVariableValue += ')' | |
return { | |
variableName, | |
defaultVariableValue | |
} | |
}); | |
// remove null values and only duplicates of the same key && value | |
// we want to keep duplicate CSS variables with different values to identify | |
// those CSS variables with different defaults, this may highlight variables in | |
// the Curam infrastructure that have a different default to the Carbon grey10 theme | |
let filteredAllCssVariableMap = allCssVariableMap.reduce((finalArray, current) => { | |
const foundDuplicate = finalArray.find(item => { | |
const dup = item.variableName === current.variableName && item.defaultVariableValue.replace(/ /g, '') === current.defaultVariableValue.replace(/ /g, ''); | |
return dup; | |
}); | |
const isInCssVarMap = window.top.cssVarMap.find(item => { | |
const dup = item.variableName === current.variableName && item.defaultVariableValue.replace(/ /g, '') === current.defaultVariableValue.replace(/ /g, ''); | |
return dup; | |
}); | |
if (!foundDuplicate && !isInCssVarMap) { | |
window.top.cssVarMap.push(current) | |
return finalArray.concat([current]); | |
} else { | |
return finalArray; | |
} | |
}, []) | |
return filteredAllCssVariableMap; | |
} | |
const updateCustomCssPropertiesList = () => { | |
const result = getCustomCssProps().sort((a, b)=> { | |
if (a.variableName < b.variableName) return -1; | |
if (a.variableName > b.variableName) return 1; | |
return 0; | |
}); | |
// display the exposed CSS variables in a list on the sidebar | |
for (let i = 0; i < result.length; i++) { | |
const propertyName = result[i].variableName | |
const propertyValue = result[i].defaultVariableValue | |
const cssVarListItem = document.createElement('li'); | |
const variableNameSpan = document.createElement('span'); | |
variableNameSpan.innerHTML = propertyName + ': ' | |
const variableValueSpan = document.createElement('span'); | |
variableValueSpan.classList.add('font-bold') | |
variableValueSpan.innerHTML = propertyValue + ';' | |
const color = document.createElement('input'); | |
color.setAttribute('type', 'color'); | |
color.setAttribute('value', propertyValue.trim()); | |
color.disabled = true; | |
cssVarListItem.append(variableNameSpan) | |
cssVarListItem.append(variableValueSpan) | |
cssVarListItem.append(color) | |
window.top.customCssPropList.append(cssVarListItem) | |
if(i === (result.length - 1)){ | |
const hr = document.createElement('hr'); | |
window.top.customCssPropList.append(hr) | |
} | |
} | |
} | |
// main | |
ready(function () { | |
console.time('SPM: custom Css properties script'); | |
// we only want to define some things once in the root window like global vars, sidebar UI, etc. | |
if(inRootWindow()) { | |
// GLOBAL VARIABLES | |
// iframeStyles is a global iframes array to store both root window | |
// and iframe window style elements. we will iterate | |
// these style elements to update CSS styling as the user | |
// types in the textarea input. this will apply custom styling | |
// to all <style> elements in all iframes. | |
window.iframeRootStyles = []; | |
window.iframeStyles = []; | |
window.rootCssOverrides = defaultRootCssOverrides; | |
window.propVarMap = [] | |
window.cssVarMap = [] | |
window.rootCssVarMap = [] | |
// FEATURE TOOGLES | |
// HOVER HIGHLIGHT | |
window.hoverHightlightFeature = false; | |
function toggleHoverHighlight() { | |
if(window.hoverHightlightFeature){ | |
window.hoverHightlightFeature = false | |
} else { | |
window.hoverHightlightFeature = true | |
} | |
} | |
const headElement = document.getElementsByTagName('head')[0]; | |
// SIDEBAR STYLE | |
const sidebarStyle = document.createElement('style') | |
sidebarStyle.type = 'text/css'; | |
// sidebar styling to overlay the caseworker app UI. | |
sidebarStyle.innerHTML = ` | |
.list-none { | |
list-style-type: none; | |
} | |
.pl-0 { | |
padding-left: 0; | |
} | |
.hide { | |
right: -34rem !important; | |
} | |
.mb-0 { | |
margin-bottom: 0; | |
} | |
.mb-2 { | |
margin-bottom: 0.5rem; | |
} | |
.mb-4 { | |
margin-bottom: 1rem; | |
} | |
.mt-0 { | |
margin-top: 0; | |
} | |
.mt-2 { | |
margin-top: 0.5rem; | |
} | |
.text-base { | |
font-size: 1rem; | |
} | |
.text-xl { | |
font-size: 1.25rem; | |
} | |
.italic { | |
font-style: italic; | |
} | |
.font-bold { | |
font-weight: 700; | |
} | |
.leading-normal { | |
line-height: 1.5rem; | |
} | |
.text-right { | |
text-align: right; | |
} | |
.block { | |
display: block; | |
} | |
.selector__name { | |
color: #393939 | |
} | |
.on { | |
background-color: #0f62fe !important; | |
} | |
.sidebar__button { | |
background-color: #393939; | |
border-color: rgba(0,0,0,0); | |
color: #fff; | |
cursor: pointer; | |
outline: none; | |
height: 2rem; | |
} | |
.sidebar__highlight-element-button { | |
background-color: #393939; | |
border-color: rgba(0,0,0,0); | |
color: #fff; | |
cursor: pointer; | |
outline: none; | |
height: 2rem; | |
} | |
.sidebar__panel { | |
position: fixed; | |
color: #161616; | |
right: 0; | |
top: 2rem; | |
height: calc(100vh - 4rem); | |
max-width: 31rem; | |
background-color: #ffffff; | |
z-index: 100; | |
border-top: 1px solid #dfe3e6; | |
border-left: 1px solid #dfe3e6; | |
padding: 1rem; | |
overflow: hidden scroll; | |
direction: ltr; | |
} | |
/* class name duplicated 3 times to incease specificity over old Curam styling */ | |
.sidebar__textarea.sidebar__textarea.sidebar__textarea { | |
width: 28rem; | |
resize: none; | |
padding: 1rem !important; | |
background-color: #f4f4f4; | |
color: #161616; | |
} | |
` | |
headElement.append(sidebarStyle); | |
// SIDEBAR PANEL UI | |
const sidebarPanel = document.createElement('div'); | |
sidebarPanel.classList.add('sidebar__panel', 'hide', 'text-base', 'leading-normal') | |
const informationLinksDiv = document.createElement('div'); | |
informationLinksDiv.classList.add('mb-4'); | |
const mdnCssCustomPropertiesLink = document.createElement('a'); | |
mdnCssCustomPropertiesLink.innerHTML = 'MDN: CSS Custom Properties'; | |
mdnCssCustomPropertiesLink.setAttribute('href', 'https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties'); | |
mdnCssCustomPropertiesLink.setAttribute('target', '_blank'); | |
mdnCssCustomPropertiesLink.classList.add('block'); | |
const carbonComponentsRefLink = document.createElement('a'); | |
carbonComponentsRefLink.innerHTML = 'Carbon Components Themes'; | |
carbonComponentsRefLink.setAttribute('href', 'https://themes.carbondesignsystem.com/'); | |
carbonComponentsRefLink.setAttribute('target', '_blank'); | |
carbonComponentsRefLink.classList.add('block'); | |
informationLinksDiv.append(mdnCssCustomPropertiesLink) | |
informationLinksDiv.append(carbonComponentsRefLink) | |
sidebarPanel.append(informationLinksDiv) | |
const sidebarTitle = document.createElement('h1'); | |
sidebarTitle.innerHTML = 'Theme'; | |
sidebarTitle.classList.add('text-xl', 'font-bold', 'mb-4', 'leading-normal'); | |
sidebarPanel.append(sidebarTitle) | |
// allow users to input their own CSS variable declarations | |
const sidebarTextarea = document.createElement('textarea'); | |
sidebarTextarea.innerHTML = window.rootCssOverrides; | |
sidebarTextarea.classList.add('sidebar__textarea', 'mb-4'); | |
sidebarTextarea.setAttribute('rows', 20); | |
// when a user types in the textarea input we need to iterate | |
// over all of the stored style elements and set the inner html | |
// to the new styling. this will apply the styling to both the main | |
// window UI and all iframes. | |
sidebarTextarea.addEventListener('input', function () { | |
let value = sidebarTextarea.value; | |
window.rootCssOverrides = value; | |
for(let iframeStyleIndex = 0; iframeStyleIndex < window.iframeRootStyles.length; iframeStyleIndex++){ | |
window.iframeRootStyles[iframeStyleIndex].innerHTML = value; | |
} | |
}) | |
// js to allow Tab to be used in textarea https://stackoverflow.com/a/6637396/2600522 | |
sidebarTextarea.addEventListener('keydown', function(e) { | |
if (e.key == 'Tab') { | |
e.preventDefault(); | |
var start = this.selectionStart; | |
var end = this.selectionEnd; | |
// set textarea value to: text before caret + tab + text after caret | |
this.value = this.value.substring(0, start) + | |
"\t" + this.value.substring(end); | |
// put caret at right position again | |
this.selectionStart = | |
this.selectionEnd = start + 1; | |
} | |
}); | |
sidebarPanel.append(sidebarTextarea) | |
const copyThemeButton = document.createElement('button'); | |
copyThemeButton.innerHTML = 'Copy theme to clipboard' | |
copyThemeButton.classList.add('sidebar__button', 'mb-4', 'leading-normal') | |
copyThemeButton.onclick = function(){ | |
document.querySelector(".sidebar__textarea").select(); | |
document.execCommand('copy'); | |
} | |
sidebarPanel.append(copyThemeButton) | |
const sidebarCustomCssVariableExampleHeading = document.createElement('h2'); | |
sidebarCustomCssVariableExampleHeading.innerHTML = 'Example'; | |
sidebarCustomCssVariableExampleHeading.classList.add('text-xl', 'font-bold', 'mb-4'); | |
sidebarPanel.append(sidebarCustomCssVariableExampleHeading); | |
// example usage of CSS properties | |
const sidebarCustomCssVariableExampleTextarea = document.createElement('textarea'); | |
sidebarCustomCssVariableExampleTextarea.innerHTML = `:root { | |
--ui-background: white; | |
--interactive-01: hotpink; | |
--text-01: black; | |
}`; | |
sidebarCustomCssVariableExampleTextarea.classList.add('sidebar__textarea', 'mb-4'); | |
sidebarCustomCssVariableExampleTextarea.setAttribute('rows', 5); | |
sidebarCustomCssVariableExampleTextarea.setAttribute('readonly', true); | |
sidebarPanel.append(sidebarCustomCssVariableExampleTextarea) | |
// const cssRootVariablesTitle = document.createElement('h1'); | |
// cssRootVariablesTitle.innerHTML = 'Existing :root CSS Properties'; | |
// cssRootVariablesTitle.classList.add('text-xl', 'font-bold', 'mb-4', 'leading-normal'); | |
// sidebarPanel.append(cssRootVariablesTitle) | |
const cssRootVarList = document.createElement('ul'); | |
cssRootVarList.classList.add('mb-4', 'list-none', 'pl-0') | |
sidebarPanel.append(cssRootVarList) | |
// store the root var list globally | |
window.cssRootVarList = cssRootVarList; | |
const cssAllVariablesTitle = document.createElement('h1'); | |
cssAllVariablesTitle.innerHTML = 'Custom CSS Properties'; | |
cssAllVariablesTitle.classList.add('text-xl', 'font-bold', 'mb-4', 'leading-normal'); | |
sidebarPanel.append(cssAllVariablesTitle); | |
const customCssPropList = document.createElement('ul'); | |
customCssPropList.classList.add('mb-4', 'list-none', 'pl-0'); | |
sidebarPanel.append(customCssPropList) | |
// store the custom css properties list globally | |
window.customCssPropList = customCssPropList; | |
const cssPropVarListTitle = document.createElement('h1'); | |
cssPropVarListTitle.innerHTML = 'Selector/Custom CSS Properties List'; | |
cssPropVarListTitle.classList.add('text-xl', 'font-bold', 'mb-4', 'leading-normal'); | |
sidebarPanel.append(cssPropVarListTitle); | |
const cssPropVarList = document.createElement('ul'); | |
cssPropVarList.classList.add('mb-4', 'list-none', 'pl-0') | |
sidebarPanel.append(cssPropVarList) | |
// store the propvar list globally | |
window.cssPropVarList = cssPropVarList; | |
// add the sidebar to the root <body> element | |
const bodyElement = document.getElementsByTagName('body')[0]; | |
bodyElement.prepend(sidebarPanel) | |
// SIDEBAR PANEL UI END | |
// TOP MENU TABS UI | |
const topLevelMenuContainer = document.createElement('div'); | |
topLevelMenuContainer.classList.add('text-right') | |
const sidebarShowHideButton = document.createElement('button'); | |
sidebarShowHideButton.innerHTML = 'Toggle theming sidebar'; | |
sidebarShowHideButton.classList.add('sidebar__button', 'leading-normal') | |
// toggle the sidebar panel by moving it out of view using the .hide class | |
sidebarShowHideButton.onclick = function() { | |
toggleSidebar(); | |
if(sidebarShowHideButton.classList.contains('on')){ | |
sidebarShowHideButton.classList.remove("on"); | |
} else { | |
sidebarShowHideButton.classList.add("on"); | |
} | |
} | |
const hoverHighlightToggle = document.createElement('button'); | |
hoverHighlightToggle.innerHTML = 'Toggle hover highlight'; | |
hoverHighlightToggle.classList.add('sidebar__button', 'leading-normal') | |
hoverHighlightToggle.onclick = function() { | |
toggleHoverHighlight(); | |
if(hoverHighlightToggle.classList.contains('on')){ | |
hoverHighlightToggle.classList.remove("on"); | |
} else { | |
hoverHighlightToggle.classList.add("on"); | |
} | |
} | |
topLevelMenuContainer.append(hoverHighlightToggle) | |
topLevelMenuContainer.append(sidebarShowHideButton) | |
bodyElement.prepend(topLevelMenuContainer); | |
// TOP MENU TABS UI END | |
const tooltip = document.createElement('div'); | |
tooltip.classList.add('infotooltip', 'hidetooltip'); | |
bodyElement.prepend(tooltip); | |
window.tooltip = tooltip; | |
} | |
// TODO: fix bug in firefox where .iframeRootStyles is undefined when this section runs. Using setTimeout(500ms) works. | |
// create style element for :root custom properties | |
const rootStyles = document.createElement('style') | |
rootStyles.type = 'text/css'; | |
rootStyles.innerHTML = window.top.rootCssOverrides; | |
// create style element for styling outside of :root | |
const otherStyles = document.createElement('style') | |
otherStyles.type = 'text/css'; | |
otherStyles.innerHTML = defaultOtherCSSOverrides; | |
const rootHead = document.getElementsByTagName('head')[0]; | |
rootHead.append(rootStyles); | |
rootHead.append(otherStyles); | |
window.top.iframeRootStyles.push(rootStyles) | |
window.top.iframeStyles.push(otherStyles) | |
// RUN IN ALL WINDOWS INCLUDING IFRAMES | |
// Existing :root CSS Properties | |
// console.time('SPM: updateExistingRootCssPropsList()'); | |
// updateExistingRootCssPropsList(); | |
// console.timeEnd('SPM: updateExistingRootCssPropsList()'); | |
// Custom CSS Properties | |
console.time('SPM: updateCustomCssPropertiesList()'); | |
updateCustomCssPropertiesList(); | |
console.timeEnd('SPM: updateCustomCssPropertiesList()'); | |
// Selector/CSS Properties List | |
console.time('SPM: updateSelectorCssPropList()'); | |
updateSelectorCssPropList(); | |
console.timeEnd('SPM: updateSelectorCssPropList()'); | |
// set event listeners | |
document.addEventListener('mouseover', function (e) { | |
if(window.top.hoverHightlightFeature){ | |
addBorderToElement(e.target) | |
} | |
}); | |
document.addEventListener('mouseout', function (e) { | |
if(window.top.hoverHightlightFeature){ | |
removeBorderFromElement(e.target) | |
} | |
}); | |
console.timeEnd('SPM: custom Css properties script'); | |
}) | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment