oda-framework
Version:
254 lines (249 loc) • 9.7 kB
JavaScript
let _vars;
class themeVars extends ROCKS({
$public: (() => {
const styles = {}, media = [];
const fn = (rule, _scope = 'no-scope', isMedia = false) => {
let scope = _scope;
if (rule.selectorText === ':root') {
styles[scope] ||= { rules: [] };
styles[scope].rules.push({ rule });
[...(rule?.style || [])].map(i => {
if (i === "--style-group") {
scope = _scope + '-' + rule.style.getPropertyValue(i).trim().replaceAll('\'', '');
styles[scope] ||= {}
}
});
[...(rule?.style || [])].map(propName => {
if (propName?.indexOf("--") === 0) {
const v = rule.style.getPropertyValue(propName).trim();
if (propName !== '--style-group') {
styles[scope].vars ||= [];
styles[scope].vars.push({ var: { [propName.trim()]: v }, isMedia });
}
}
});
}
}
[...top.document.styleSheets].map(sheet => {
const scope = sheet.ownerNode.getAttribute('scope') || 'no-scope';
[...sheet.cssRules].map(rule => {
fn(rule, scope);
if (rule?.media?.mediaText.includes("prefers-color-scheme")) {
[...rule.cssRules].map(rule => fn(rule, scope, true));
}
})
})
// console.log(styles)
let vars = {}, groups = [];
Object.keys(styles).map(s => {
styles[s].vars?.map(v => {
const key = Object.keys(v.var)[0],
val = Object.values(v.var)[0],
$group = key.split('-')[2];
groups[$group] ||= [];
groups[$group].push($group);
vars[key] = {
$public: true,
$group,
$editor: key.includes('color') || key.includes('background') ? 'oda-theme-editor-color-picker' : 'oda-pg-string',
$def: val,
set: (n) => updateStyle(key, n)
}
})
})
Object.values(vars).map(i => {
i.$group = groups[i.$group]?.length > 1 ? i.$group : ' ';
})
// console.log(vars);
_vars = vars;
return vars;
})()
}) { }
let changes = {},
isDark = false,
themeEditor;
const updateStyle = (key, val) => {
let newVal = val;
changes[key] ||= {};
changes[key].defVal ||= _vars[key].$def;
if (_vars[key].$def?.includes('light-dark')) {
val = val.replaceAll(',', ' ');
// console.log(_vars[key].$def);
let v = (_vars[key].$def).split('light-dark(');
if (isDark) {
val = 'light-dark(' + v[1].split(',')[0] + ', ' + val + ')';
} else {
val = 'light-dark(' + val + ', ' + v[1].split(',')[1];
}
// console.log(val)
}
ODA.updateStyle({ [key]: val });
changes[key].val = val;
changes[key].isDark = isDark;
changes[key].newVal = newVal;
if (themeEditor) {
themeEditor.vars[key] = newVal;
themeEditor.isChanged = true;
}
// console.log(changes)
}
ODA({ is: 'oda-theme-editor', imports: '@tools/property-grid, @oda/color-picker',
template: `
<style>
:host {
--vertical;
--flex;
background: var(--content-background);
}
</style>
<div class="horizontal" ~if="!hideSwitchBtn" style="align-items: center; margin: 8px;" >
<div>Switch Light/Dark mode: </div>
<oda-toggle size="24" ::toggled></oda-toggle>
<oda-button class="border" ="clearChanges">Clear changes</oda-button>
</div>
<div class="horizontal wrap no-flex" style=" justify-content: center; position: relative; overflow: auto; flex-wrap: wrap; white-space:wrap; overflow-y: auto;">
<oda-button ~class="$for.item.k" class="border" ~for="btns" style="pointer-events: unset; position: unset; min-width: 240px; height: 80px; margin: 4px; font-size: larger; padding: 4px;" ="showPropertyGrid($for.item)">
{{$for.item.k}}
</oda-button>
</div>
<oda-property-grid slot="right-panel" class="vertical flex border" label="Theme settings" :inspected-object="vars" style="padding:0"></oda-property-grid>
`,
toggled: {
$def: false,
set(n) {
isDark = n;
this.theme = n ? 'dark' : 'light';
this.switchTheme();
}
},
hideSwitchBtn: false,
$public: {
theme: {
$def: 'light',
$list: ['light', 'dark', 'light dark'],
set(n) {
this.toggled = n === 'dark';
}
}
},
vars: { $def() { return new themeVars() } },
get btns() {
const btns = [];
Object.keys(cssRules).map(k => {
if (cssRules[k].includes('var('))
btns.push({ k: k.slice(2), v: cssRules[k] });
})
btns.sort((a, b) => a.k < b.k ? -1 : a.k > b.k ? 1 : 0);
// console.log(btns);
return btns;
},
async showPropertyGrid(item = []) {
const props = new class props extends ROCKS({
$public: (() => {
const props = {};
const vars = item.v.split('\n');
vars.map(i => {
if (i.includes('var')) {
const key = i.split('var(')[1].split(')')[0].split(',')[0];
props[key] = {
$group: item.k,
$public: true,
$editor: key?.includes('color') || key?.includes('background') ? 'oda-theme-editor-color-picker' : 'oda-pg-string',
$def: changes[key]?.val || this.vars[key],
set: (n) => { updateStyle(key, n) }
}
}
})
return props;
})()
}) { }
const res = await ODA.showDropdown('oda-property-grid', { inspectedObject: props }, { anchor: 'right-top', align: 'left', title: 'Custom property\'s', minWidth: '480px' });
},
get changes() {
return changes;
},
set changes(v) {
changes = v;
},
get isDark() {
return isDark;
},
isChanged: false,
isLoadChanges: false,
clearChanges() {
this.vars = new themeVars();
this.btns = undefined;
Object.keys(changes || {}).map(key => {
ODA.updateStyle({ [key]: changes[key].defVal });
})
this.changes = changes = {};
this.isChanged = this.isLoadChanges;
},
async switchTheme(theme = this.theme) {
let wins = [top];
wins = [...wins, ...Array.from(top)];
wins.map(w => {
let meta = w.document.getElementById('color-scheme');
if (!meta) {
meta = document.createElement('meta');
meta.id = meta.name = "color-scheme";
w.document.head.appendChild(meta);
}
meta.content = theme;
})
this.async(() => {
this.vars = new themeVars();
this.btns = undefined;
Object.keys(changes || {}).map(key => {
if (changes[key].isDark === this.toggled)
this.vars[key] = changes[key].newVal;
})
}, 100)
return theme;
},
pgChanged(e) {
console.log(e)
},
ready() {
themeEditor = this;
this.theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
this.toggled = this.theme === 'dark';
this.switchTheme();
}
})
ODA({ is: 'oda-theme-editor-color-picker', imports: '@oda/color-picker-oklch',
template: `
<style>
:host {
--horizontal;
--flex;
position: relative;
}
</style>
<div style="display: block; margin: 4px;align-items: center; width: calc(100% - 40px); overflow: hidden; white-space: nowrap; text-overflow: ellipsis;">{{value}}</div>
<div class="border" style="width: 40px; margin: 4px; cursor: pointer;" ~style="{background: value}" ="openPicker"></div>
`,
value: '',
async openPicker(e) {
let val = this.value,
light, dark;
if (val.includes('light-dark')) {
let v = val.replace('light-dark(', '').split(',');
dark = v[1].replace(')', '');
light = v[0];
val = isDark ? dark : light;
}
const el = ODA.createElement('oda-color-picker-oklch');
el.listen('color-changed', e => {
this.throttle('colorChanged', () => {
this.value = e.detail.value;
// console.log(this.value);
}, 50)
})
let res = await ODA.showDropdown(el, { value: val, srcValue: val }, { anchor: 'right-top', align: 'left', parent: e.target, title: 'Select color' });
res = res?.result;
if (res) {
this.value = res;
}
}
})