UNPKG

oda-framework

Version:

It's an ES Progressive Framework based on the technology of Web Components and designed especially for creating custom UI/UX of any complexity for web and cross-platform PWA mobile applications.

254 lines (249 loc) 9.7 kB
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 { @apply --vertical; @apply --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" @tap="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;" @tap="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 { @apply --horizontal; @apply --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}" @tap="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; } } })