oda-framework
Version:
700 lines (637 loc) • 19.3 kB
JavaScript
let style = /*css*/`
:root {
--main-color: indigo;
--main-color-invert: oklch(from var(--main-color) 1 1 180);
--header-1: oklch(from var(--main-color) 0.8 .07 h);
--header-2: oklch(from var(--main-color) 0.4 .07 h);
--header-background: light-dark(var(--header-1), var(--header-2));
--header-color: light-dark(var(--header-2), var(--header-1));
--content-1: oklch(from var(--main-color) 1 .02 h);
--content-2: oklch(from var(--main-color) 0.2 .02 h);
--content-background: light-dark(var(--content-1), var(--content-2));
--content-color: light-dark(var(--content-2), var(--content-1));
--dark-1: oklch(from var(--main-color) .6 .05 h);
--dark-2: oklch(from var(--main-color) .8 .05 h);
--dark-background: light-dark(var(--dark-1), var(--dark-2));
--dark-color: light-dark(var(--dark-2), var(--dark-1));
--light-1: oklch(from var(--main-color) .95 .05 h);
--light-2: oklch(from var(--main-color) .5 .05 h);
--light-background: light-dark(var(--light-1), var(--light-2));
--light-color: light-dark(var(--light-2), var(--light-1));
--body-1: oklch(from var(--main-color) 1 .02 h);
--body-2: oklch(from var(--main-color) 0.2 .02 h);
--body-background: light-dark(transparent, transparent);
--body-color: light-dark(var(--body-2), var(--body-1));
--section-1: oklch(from var(--main-color) .8 .02 h);
--section-2: oklch(from var(--main-color) 0.2 .02 h);
--section-background: light-dark(var(--section-1), var(--section-2));
--section-color: light-dark(var(--section-2), var(--section-1));
--layout-1: oklch(from var(--main-color) 1 .02 h);
--layout-2: oklch(from var(--main-color) 0.1 .02 h);
--layout-background: light-dark(var(--layout-1), var(--layout-2));
--layout-color: light-dark(var(--layout-2), var(--layout-1));
--info-1: oklch(from var(--main-color) 1 .02 h);
--info-2: oklch(from var(--main-color) 0.6 .2 h);
--info-background: light-dark(var(--info-1), var(--info-2));
--info-color: light-dark(var(--info-2), var(--info-1));
--focused-color: light-dark(var(--main-color), var(--main-color-invert));
--accent-color: light-dark(var(--main-color), var(--main-color-invert));
--accent-background: light-dark(var(--main-color-invert), var(--main-color));
--bar-background: var(--content-background);
--stroke-color: light-dark(transparent, transparent);
--border-color: light-dark(black, white);
--shadow-color: light-dark(rgba(0,0,0,.2), rgba(255,255,255,.2));
--success-color: light-dark(green, white);
--success-background: light-dark(white, green);
--error-color: light-dark(red, yellow);
--error-background: light-dark(yellow, red);
--warning-color: light-dark(orange, wheat);
--warning-background: light-dark(wheat, orange);
--disabled-color: light-dark(silver, silver);
--selected-color: light-dark(var(--light-1), var(--light-2));
--selected-background: light-dark(var(--light-2), var(--light-1));
--selected-filter: brightness(0.8) contrast(1.2);
--pointer-color: light-dark(var(--main-color), var(--main-color-invert));
}
:root{
--font-family: Roboto, Noto, sans-serif;
--font-150:{
font-size: 150%;
};
}
:root{
--header: {
background-color: var(--header-background);
color: var(--header-color);
fill: var(--header-color);
border-color: var(--header-color);
};
--dark: {
background-color: var(--dark-background) !important;
color: var(--dark-color) !important;
fill: var(--dark-color) !important;
border-color: var(--dark-color) !important;
};
--content:{
background-color: var(--content-background);
color: var(--content-color);
fill: var(--content-color);
border-color: var(--dark-color);
};
--light:{
background-color: var(--light-background) !important;
color: var(--light-color) !important;
fill: var(--light-color) !important;
border-color: var(--light-color) !important;
};
--boxed: {
border: 1px solid darkgray;
margin: 4px;
padding: 4px;
};
--horizontal: {
display: flex;
flex-direction: row;
};
--horizontal-center:{
@apply --horizontal;
align-items: center;
};
--h:{
@apply --horizontal;
};
--horizontal-end:{
@apply --horizontal;
justify-content: flex-end;
};
--bold:{
font-weight: bold;
};
--between:{
justify-content: space-between;
};
--flex:{
flex: 1;
flex-basis: auto;
};
--no-flex: {
flex-grow: 0;
flex-shrink: 0;
flex-basis: auto;
};
--bar:{
@apply --horizontal;
};
--center:{
justify-content: center;
align-self: center;
align-content: center;
align-items: center;
};
--vertical: {
display: flex;
flex-direction: column;
};
--border:{
border: 1px solid;
box-sizing: border-box;
};
--outline:{
outline: 1px solid;
};
--toolbar:{
@apply --horizontal;
align-items: center;
};
--heading: {
@apply --horizontal;
align-items: center;
justify-content: space-between;
background-color: var(--content-color) !important;
color: var(--content-background) !important;
fill: var(--content-background) !important;
};
--layout: {
background: var(--layout-background);
color: var(--layout-color);
fill: var(--layout-color);
};
--footer: {
@apply --header;
};
--border-radius: 0px;
--border: {
border: 1px solid var(--border-color, black);
border-radius: var(--border-radius);
};
--border-left: {
border-left: 1px solid var(--border-color, black);
};
--border-top: {
border-top: 1px solid var(--border-color, black);
};
--border-right: {
border-right: 1px solid var(--border-color, black);
};
--border-bottom: {
border-bottom: 1px solid var(--border-color, black);
};
--label: {
white-space: nowrap;
align-content: center;
text-overflow: ellipsis;
font-family: var(--font-family);
};
--cover:{
position: fixed;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,.1);
z-index: 1000;
};
--user-select:{
user-select: text !important;
}
}
html {
touch-action: none;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
height: 100%;
--my-variable: 100px;
}
@media print {
.pe-no-print {
display: none !important;
}
.pe-preserve-ancestor {
height: auto !important;
overflow: visible !important;
display: block !important;
margin: 0 !important;
padding: 0 !important;
border: none !important;
box-shadow: none !important;
}
*::-webkit-scrollbar {
width: 0px; height: 0px;
}
.raised, [raised] {
border: none !important;
box-shadow: none !important;
}
table, pre, code-block {
break-inside: avoid;
}
h1, h2, h3 {
break-after: avoid;
}
section {
break-before: auto;
}
}
input{
background-color: var(--content-background);
color: var(--content-color);
}
input::placeholder{
color: inherit;
opacity: .5;
}
::part{
min-width: 0px;
}
::part(error){
position: relative;
overflow: visible;
min-height: 20px;
min-width: 20px;
}
::part(error):before{
content: '';
position: absolute;
top: 0px;
left: 0px;
width: 0px;
height: 0px;
border: 4px solid transparent;
border-left: 4px solid red;
border-top: 4px solid red;
}
body{
display: flex;
flex: 1;
flex-direction: column;
font-family: var(--font-family);
user-select: none;
margin: 0px;
padding: 0px;
height: 100%;
overscroll-behavior: contain;
}
[hidden]{
display: none !important;
}
:root {
--shadow: {
box-shadow: 0 8px 10px 1px var(--shadow-color), 0 3px 14px 2px var(--shadow-color), 0 5px 5px -3px var(--shadow-color);
};
--shadow-transition: {
transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
};
--text-shadow: {
text-shadow: 0 1px 1px var(--header-background, gray);
};
--text-shadow-black: {
text-shadow: 0 1px 1px black;
};
--raised:{
box-shadow: 0 0 3px 0 var(--shadow-color);
};
}
body[context-menu-show] *:not(oda-context-menu){
pointer-events: none;
}
:root {
--invert:{
filter: invert(1);
};
--error: {
color: var(--error-color) !important;
border-color: var(--error-color) !important;
fill: var(--error-color) !important;
background-color: var(--error-background) !important;
};
--error-invert: {
color: var(--error-background) !important;
border-color: var(--error-background) !important;
fill: var(--error-background) !important;
background-color: var(--error-color) !important;
};
--error-before: {
content: attr(error);
background-image: url("/web/oda/tools/styles/error.png");
background-size: contain;
background-repeat: no-repeat;
position: absolute;
top: -6px;
left: 6px;
pointer-events: none;
@apply --raised;
@apply --error;
@apply --content;
font-size: xx-small;
padding: 2px 2px 2px 16px;
z-index: 1;
@apply --border;
border-radius: 6px;
min-height: 10px;
};
--success: {
color: var(--success-color) !important;
fill: var(--success-color) !important;
border-color: var(--success-color) !important;
background-color: var(--success-background) !important;
};
--success-invert: {
background-color: var(--success-color) !important;
fill: var(--success-background) !important;
color: var(--success-background) !important;
border-color: var(--success-background) !important;
};
--info: {
background-color: var(--info-background) !important;
color: var(--info-color) !important;
fill: var(--info-color) !important;
border-color: var(--info-color) !important;
};
--info-invert: {
background-color: var(--info-color) !important;
fill: var(--info-background) !important;
color: var(--info-background) !important;
border-color: var(--info-background) !important;
};
--warning: {
background-color: var(--warning-background) !important;
color: var(--warning-color) !important;
fill: var(--warning-color) !important;
border-color: var(--warning-color) !important;
};
--warning-invert: {
background-color: var(--warning-color) !important;
color: var(--warning-background) !important;
fill: var(--warning-background) !important;
border-color: var(--warning-background) !important;
};
--accent: {
color: var(--accent-color) !important;
fill: var(--accent-color) !important;
border-color: var(--accent-color) !important;
background-color: var(--accent-background) !important;
};
--accent-invert: {
background-color: var(--accent-color) !important;
fill: var(--accent-background) !important;
color: var(--accent-background) !important;
border-color: var(--accent-background) !important;
};
--help: {
};
--help-after: {
content: attr(help);
background-image: url("/web/oda/tools/styles/help.png");
background-size: contain;
background-repeat: no-repeat;
position: absolute;
top: -6px;
left: 6px;
pointer-events: none;
@apply --raised;
@apply --error;
@apply --content;
font-size: xx-small;
padding: 2px 2px 2px 16px;
z-index: 1;
@apply --border;
border-radius: 6px;
min-height: 10px;
};
}
:root{
--active: {
color: var(--selected-color) !important;
fill: var(--selected-color) !important;
background-color: var(--selected-background) !important;
};
--selected: {
color: var(--selected-color) !important;
fill: var(--selected-color) !important;
filter: var(--selected-filter);
background: var(--selected-background) !important;
};
--outlined: {
outline: var(--content-color) dashed .5px;
outline-offset: -1px;
};
--focused:{
position: relative !important;
};
--dimmed: {
opacity: 0.7;
filter: grayscale(80%);
};
--disabled: {
@apply --dimmed;
opacity: 0.3;
cursor: default !important;
user-focus: none;
user-focus-key: none;
user-select: none;
user-input: none;
pointer-events: none;
};
}
.focused:after, *[focused]:after{
content: '';
position: absolute;
background-color: var(--focused-color);
bottom: 0px;
left: 0px;
height: 2px;
right: 0px;
z-index: 1;
}
.focused.focused-left:after, *[focused].focused-left:after{
bottom: 0px;
left: 0px;
width: 2px;
top: 0px;
right: unset;
height: unset;
}
.focused.focused-right:after, *[focused].focused-right:after{
bottom: 0px;
top: 0px;
width: 2px;
right: 0px;
left: unset;
height: unset;
}
.focused.focused-top:after, *[focused].focused-top:after{
left: 0px;
top: 0px;
right: 0px;
bottom: unset;
}
@keyframes blinker {
100% {
opacity: 0;
}
}
@-webkit-keyframes blinker {
100% {
opacity: 0;
}
}
@keyframes zoom-in {
from {transform:scale(0)}
to {transform:scale(1)}
}
@keyframes zoom-out {
from {transform:scale(1)}
to {transform:scale(0)}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@-moz-keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@-moz-keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-thumb {
border-radius: 4px;
background-color: var(--light-background);
-webkit-box-shadow: inset 0 0 4px var(--shadow-color);
}
::-webkit-scrollbar-thumb:hover {
background-color: var(--header-background);
}
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 4px var(--shadow-color);
background-color: var(--content-background);
}
`;
globalThis.cssRules = [];
globalThis.adopted = [];
const APPLY_REG_EXP = /@apply\s*/gm;
const COMMENT_REG_EXP = /\/\*[^*/]*\*\//igm;
const VAR_REG_EXP = /^--[\w-]*\w+/;
const RULE_BODY_REG_EXP = /^\s*{\s*[^{}]*\s*}\s*$/;
function extractCSSRules (style){
const result = []
const adds = []
if (typeof style === 'string'){
let ss = new CSSStyleSheet();
ss.replaceSync(style);
style = ss;
}
for (let i of style.cssRules) {
if (i?.selectorText)
result.push(i.selectorText+ '{');
else{
adds.unshift(applyStyleMixins(i.cssText));
continue
}
for (let key of i.style || []) {
let val = i.style.getPropertyValue(key).toString().trim();
const priority = i.style.getPropertyPriority(key);
if (priority) {
val = `${val} !${priority}`;
}
if (!val) continue;
if (!VAR_REG_EXP.test(key) || !RULE_BODY_REG_EXP.test(val)) {
result.push(key+': '+val+';')
continue;
}
val = val.replace(/^{|}$/g, '');
val = applyStyleMixins(val);
val = val.trim().split(';').map(i => {
return i.trim();
}).join(';\n\t').trim();
cssRules[key] ??= val;
key = key.substring(2);
adds.push(`.${key}, [${key}] {`)
adds.push(`${val}`)
adds.push(`}`)
}
result.push('}');
}
result.push(...adds);
return result.join('');
}
function applyStyleMixins (styleText) {
styleText = styleText.replace(COMMENT_REG_EXP, '');
styleText = styleText.split(APPLY_REG_EXP);
styleText = styleText.map(i=>{
let v = i.match(VAR_REG_EXP)?.[0];
if(!v)
return i;
return i.replace(v+';', cssRules[v]);
});
styleText = styleText.join('');
return styleText;
}
const PARSE_RULE_REG_EXP = /([a-z-]+)\s*:\s*((?:[^;]*url\(.*?\)[^;]*|[^;]*)*)\s*(?:;|$)/gi;
function cssRuleParse (rules, res, host = false) {
for (let rule of rules) {
switch (rule.type){
case CSSRule.KEYFRAMES_RULE:{
let key = rule.cssText;
let r = res[key] = res[key] || {};
cssRuleParse(rule.cssRules, r);
} break;
case CSSRule.MEDIA_RULE:{
let key = '@media ' + rule.media.mediaText;
let r = res[key] = res[key] || {};
cssRuleParse(rule.cssRules, r);
} break;
default:{
if (rule.cssText.includes(':host') && !host) continue;
const ss = rule.cssText.replace(rule.selectorText, '').match(PARSE_RULE_REG_EXP);
if (!ss) continue;
let sel = rule.selectorText?.split(',').join(',\n');
let r = res[sel] = res[sel] || [];
r.add(...ss);
} break;
}
}
}
style = extractCSSRules(style)
const ss = document.createElement('style');
ss.textContent = style;
ss.setAttribute('scope', 'ODA');
document.head.appendChild(ss);
import './adoptedStyleSheets.js'; // https://github.com/calebdwilliams/construct-style-sheets
if ('adoptedStyleSheets' in Document.prototype) {
let ss = new CSSStyleSheet();
ss.replaceSync(style);
globalThis.adopted = [ss];
}
export default {
cssRules: globalThis.cssRules,
adopted: globalThis.adopted,
extractCSSRules,
applyStyleMixins,
cssRuleParse
};