update, should inherit stuff properly now.
This commit is contained in:
parent
d4795d2e3c
commit
c34590c439
44
src/App.scss
44
src/App.scss
|
@ -371,8 +371,7 @@ nav {
|
||||||
border-radius: $fallback--btnRadius;
|
border-radius: $fallback--btnRadius;
|
||||||
border-radius: var(--btnRadius, $fallback--btnRadius);
|
border-radius: var(--btnRadius, $fallback--btnRadius);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
box-shadow: $fallback--buttonShadow;
|
box-shadow: var(--shadow);
|
||||||
box-shadow: var(--buttonShadow);
|
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
font-family: var(--interfaceFont, sans-serif);
|
font-family: var(--interfaceFont, sans-serif);
|
||||||
|
@ -383,25 +382,14 @@ nav {
|
||||||
|
|
||||||
i[class*="icon-"],
|
i[class*="icon-"],
|
||||||
.svg-inline--fa {
|
.svg-inline--fa {
|
||||||
color: $fallback--text;
|
color: var(--icon);
|
||||||
color: var(--btnText, $fallback--text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-moz-focus-inner {
|
&::-moz-focus-inner {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
|
||||||
box-shadow: 0 0 4px rgb(255 255 255 / 30%);
|
|
||||||
box-shadow: var(--buttonHoverShadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
box-shadow:
|
|
||||||
0 0 4px 0 rgb(255 255 255 / 30%),
|
|
||||||
0 1px 0 0 rgb(0 0 0 / 20%) inset,
|
|
||||||
0 -1px 0 0 rgb(255 255 255 / 20%) inset;
|
|
||||||
box-shadow: var(--buttonPressedShadow);
|
|
||||||
color: $fallback--text;
|
color: $fallback--text;
|
||||||
color: var(--btnPressedText, $fallback--text);
|
color: var(--btnPressedText, $fallback--text);
|
||||||
background-color: $fallback--fg;
|
background-color: $fallback--fg;
|
||||||
|
@ -487,7 +475,12 @@ nav {
|
||||||
}
|
}
|
||||||
|
|
||||||
input,
|
input,
|
||||||
textarea,
|
textarea {
|
||||||
|
border: none;
|
||||||
|
display: inline-block;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
&.unstyled {
|
&.unstyled {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
@ -501,15 +494,7 @@ textarea,
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: $fallback--inputRadius;
|
border-radius: $fallback--inputRadius;
|
||||||
border-radius: var(--inputRadius, $fallback--inputRadius);
|
border-radius: var(--inputRadius, $fallback--inputRadius);
|
||||||
box-shadow:
|
box-shadow: var(--shadow);
|
||||||
0 1px 0 0 rgb(0 0 0 / 20%) inset,
|
|
||||||
0 -1px 0 0 rgb(255 255 255 / 20%) inset,
|
|
||||||
0 0 2px 0 rgb(0 0 0 / 100%) inset;
|
|
||||||
box-shadow: var(--inputShadow);
|
|
||||||
background-color: $fallback--fg;
|
|
||||||
background-color: var(--input, $fallback--fg);
|
|
||||||
color: $fallback--lightText;
|
|
||||||
color: var(--inputText, $fallback--lightText);
|
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
font-family: var(--inputFont, sans-serif);
|
font-family: var(--inputFont, sans-serif);
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
|
@ -561,11 +546,9 @@ textarea,
|
||||||
width: 1.1em;
|
width: 1.1em;
|
||||||
height: 1.1em;
|
height: 1.1em;
|
||||||
border-radius: 100%; // Radio buttons should always be circle
|
border-radius: 100%; // Radio buttons should always be circle
|
||||||
box-shadow: 0 0 2px black inset;
|
background-color: var(--background);
|
||||||
box-shadow: var(--inputShadow);
|
box-shadow: var(--shadow);
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
background-color: $fallback--fg;
|
|
||||||
background-color: var(--input, $fallback--fg);
|
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
|
@ -578,8 +561,9 @@ textarea,
|
||||||
|
|
||||||
&[type="checkbox"] {
|
&[type="checkbox"] {
|
||||||
&:checked + label::before {
|
&:checked + label::before {
|
||||||
color: $fallback--text;
|
color: var(--text);
|
||||||
color: var(--inputText, $fallback--text);
|
background-color: var(--background);
|
||||||
|
box-shadow: var(--shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
|
|
|
@ -1,11 +1,34 @@
|
||||||
|
const border = (top, shadow) => ({
|
||||||
|
x: 0,
|
||||||
|
y: top ? 1 : -1,
|
||||||
|
blur: 0,
|
||||||
|
spread: 0,
|
||||||
|
color: shadow ? '#000000' : '#FFFFFF',
|
||||||
|
alpha: 0.2,
|
||||||
|
inset: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const buttonInsetFakeBorders = [border(true, false), border(false, true)]
|
||||||
|
const inputInsetFakeBorders = [border(true, true), border(false, false)]
|
||||||
|
|
||||||
|
const hoverGlow = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
blur: 4,
|
||||||
|
spread: 0,
|
||||||
|
color: '--text',
|
||||||
|
alpha: 1
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Button',
|
name: 'Button',
|
||||||
selector: '.btn',
|
selector: '.button-default',
|
||||||
states: {
|
states: {
|
||||||
disabled: ':disabled',
|
disabled: ':disabled',
|
||||||
toggled: '.toggled',
|
toggled: '.toggled',
|
||||||
pressed: ':active',
|
pressed: ':active',
|
||||||
hover: ':hover'
|
hover: ':hover',
|
||||||
|
focused: ':focus-within'
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
danger: '.danger',
|
danger: '.danger',
|
||||||
|
@ -20,14 +43,49 @@ export default {
|
||||||
{
|
{
|
||||||
component: 'Button',
|
component: 'Button',
|
||||||
directives: {
|
directives: {
|
||||||
background: '--fg'
|
background: '--fg',
|
||||||
|
shadow: [{
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
blur: 2,
|
||||||
|
spread: 0,
|
||||||
|
color: '#000000',
|
||||||
|
alpha: 1
|
||||||
|
}, ...buttonInsetFakeBorders]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Button',
|
component: 'Button',
|
||||||
state: ['hover'],
|
state: ['hover'],
|
||||||
directives: {
|
directives: {
|
||||||
background: '#FFFFFF'
|
shadow: [hoverGlow, ...buttonInsetFakeBorders]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Button',
|
||||||
|
state: ['hover', 'pressed'],
|
||||||
|
directives: {
|
||||||
|
background: '--accent,-24.2',
|
||||||
|
shadow: [hoverGlow, ...inputInsetFakeBorders]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Button',
|
||||||
|
state: ['disabled'],
|
||||||
|
directives: {
|
||||||
|
background: '$blend(--background, 0.25, --parent)',
|
||||||
|
shadow: [...buttonInsetFakeBorders]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Text',
|
||||||
|
parent: {
|
||||||
|
component: 'Button',
|
||||||
|
state: ['disabled']
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
textOpacity: 0.25,
|
||||||
|
textOpacityMode: 'blend'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
export default {
|
||||||
|
name: 'DropdownMenu',
|
||||||
|
selector: '.dropdown',
|
||||||
|
validInnerComponents: [
|
||||||
|
'Text',
|
||||||
|
'Icon',
|
||||||
|
'Input'
|
||||||
|
],
|
||||||
|
states: {
|
||||||
|
hover: ':hover'
|
||||||
|
},
|
||||||
|
defaultRules: [
|
||||||
|
{
|
||||||
|
directives: {
|
||||||
|
background: '--fg'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
const border = (top, shadow) => ({
|
||||||
|
x: 0,
|
||||||
|
y: top ? 1 : -1,
|
||||||
|
blur: 0,
|
||||||
|
spread: 0,
|
||||||
|
color: shadow ? '#000000' : '#FFFFFF',
|
||||||
|
alpha: 0.2,
|
||||||
|
inset: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const inputInsetFakeBorders = [border(true, true), border(false, false)]
|
||||||
|
|
||||||
|
const hoverGlow = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
blur: 4,
|
||||||
|
spread: 0,
|
||||||
|
color: '--text',
|
||||||
|
alpha: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Input',
|
||||||
|
selector: '.input',
|
||||||
|
states: {
|
||||||
|
disabled: ':disabled',
|
||||||
|
pressed: ':active',
|
||||||
|
hover: ':hover',
|
||||||
|
focused: ':focus-within'
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
danger: '.danger',
|
||||||
|
unstyled: '.unstyled',
|
||||||
|
sublime: '.sublime'
|
||||||
|
},
|
||||||
|
validInnerComponents: [
|
||||||
|
'Text'
|
||||||
|
],
|
||||||
|
defaultRules: [
|
||||||
|
{
|
||||||
|
directives: {
|
||||||
|
background: '--fg',
|
||||||
|
shadow: [{
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
blur: 2,
|
||||||
|
spread: 0,
|
||||||
|
color: '#000000',
|
||||||
|
alpha: 1
|
||||||
|
}, ...inputInsetFakeBorders]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
state: ['hover'],
|
||||||
|
directives: {
|
||||||
|
shadow: [hoverGlow, ...inputInsetFakeBorders]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -6,13 +6,14 @@ export default {
|
||||||
'Link',
|
'Link',
|
||||||
'Icon',
|
'Icon',
|
||||||
'Button',
|
'Button',
|
||||||
'PanelHeader'
|
'Input',
|
||||||
|
'PanelHeader',
|
||||||
|
'DropdownMenu'
|
||||||
],
|
],
|
||||||
defaultRules: [
|
defaultRules: [
|
||||||
{
|
{
|
||||||
component: 'Panel',
|
|
||||||
directives: {
|
directives: {
|
||||||
background: '--fg'
|
background: '--bg'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -12,7 +12,6 @@ export default {
|
||||||
component: 'PanelHeader',
|
component: 'PanelHeader',
|
||||||
directives: {
|
directives: {
|
||||||
background: '--fg'
|
background: '--fg'
|
||||||
// opacity: 0.9
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
export default {
|
||||||
|
name: 'Popover',
|
||||||
|
selector: '.popover',
|
||||||
|
validInnerComponents: [
|
||||||
|
'Text',
|
||||||
|
'Link',
|
||||||
|
'Icon',
|
||||||
|
'Button',
|
||||||
|
'Input',
|
||||||
|
'PanelHeader',
|
||||||
|
'DropdownMenu'
|
||||||
|
],
|
||||||
|
defaultRules: [
|
||||||
|
{
|
||||||
|
directives: {
|
||||||
|
background: '--fg'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
export default {
|
||||||
|
name: 'Root',
|
||||||
|
selector: ':root',
|
||||||
|
validInnerComponents: [
|
||||||
|
'Underlay',
|
||||||
|
'TopBar',
|
||||||
|
'Popover'
|
||||||
|
],
|
||||||
|
defaultRules: [
|
||||||
|
{
|
||||||
|
directives: {
|
||||||
|
background: '--bg',
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
export default {
|
||||||
|
name: 'TopBar',
|
||||||
|
selector: 'nav',
|
||||||
|
validInnerComponents: [
|
||||||
|
'Link',
|
||||||
|
'Text',
|
||||||
|
'Icon',
|
||||||
|
'Button',
|
||||||
|
'Input'
|
||||||
|
],
|
||||||
|
defaultRules: [
|
||||||
|
{
|
||||||
|
directives: {
|
||||||
|
background: '--fg'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
export default {
|
export default {
|
||||||
name: 'Underlay',
|
name: 'Underlay',
|
||||||
selector: '#content',
|
selector: 'body', // Should be '#content' but for now this is better for testing until I have proper popovers and such...
|
||||||
outOfTreeSelector: '.underlay',
|
outOfTreeSelector: '.underlay',
|
||||||
validInnerComponents: [
|
validInnerComponents: [
|
||||||
'Panel'
|
'Panel'
|
||||||
|
|
|
@ -6,6 +6,10 @@
|
||||||
background-color: $fallback--bg;
|
background-color: $fallback--bg;
|
||||||
background-color: var(--bg, $fallback--bg);
|
background-color: var(--bg, $fallback--bg);
|
||||||
|
|
||||||
|
.panel-heading {
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
&::after,
|
&::after,
|
||||||
& {
|
& {
|
||||||
border-radius: $fallback--panelRadius;
|
border-radius: $fallback--panelRadius;
|
||||||
|
@ -131,12 +135,9 @@
|
||||||
align-items: start;
|
align-items: start;
|
||||||
// panel theme
|
// panel theme
|
||||||
color: var(--panelText);
|
color: var(--panelText);
|
||||||
background-color: $fallback--bg;
|
|
||||||
background-color: var(--bg, $fallback--bg);
|
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
background-color: $fallback--fg;
|
background-color: var(--background);
|
||||||
background-color: var(--panel, $fallback--fg);
|
|
||||||
z-index: -2;
|
z-index: -2;
|
||||||
border-radius: $fallback--panelRadius $fallback--panelRadius 0 0;
|
border-radius: $fallback--panelRadius $fallback--panelRadius 0 0;
|
||||||
border-radius: var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius) 0 0;
|
border-radius: var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius) 0 0;
|
||||||
|
|
|
@ -20,8 +20,8 @@ export const applyTheme = (input) => {
|
||||||
|
|
||||||
styleSheet.toString()
|
styleSheet.toString()
|
||||||
styleSheet.insertRule(`:root { ${rules.radii} }`, 'index-max')
|
styleSheet.insertRule(`:root { ${rules.radii} }`, 'index-max')
|
||||||
styleSheet.insertRule(`:root { ${rules.colors} }`, 'index-max')
|
// styleSheet.insertRule(`:root { ${rules.colors} }`, 'index-max')
|
||||||
styleSheet.insertRule(`:root { ${rules.shadows} }`, 'index-max')
|
// styleSheet.insertRule(`:root { ${rules.shadows} }`, 'index-max')
|
||||||
styleSheet.insertRule(`:root { ${rules.fonts} }`, 'index-max')
|
styleSheet.insertRule(`:root { ${rules.fonts} }`, 'index-max')
|
||||||
themes3.css.forEach(rule => {
|
themes3.css.forEach(rule => {
|
||||||
console.log(rule)
|
console.log(rule)
|
||||||
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
export default [
|
||||||
|
'bg',
|
||||||
|
'wallpaper',
|
||||||
|
'fg',
|
||||||
|
'text',
|
||||||
|
'underlay',
|
||||||
|
'link',
|
||||||
|
'accent',
|
||||||
|
'faint',
|
||||||
|
'faintLink',
|
||||||
|
'postFaintLink',
|
||||||
|
|
||||||
|
'cBlue',
|
||||||
|
'cRed',
|
||||||
|
'cGreen',
|
||||||
|
'cOrange',
|
||||||
|
|
||||||
|
'profileBg',
|
||||||
|
'profileTint',
|
||||||
|
|
||||||
|
'highlight',
|
||||||
|
'highlightLightText',
|
||||||
|
'highlightPostLink',
|
||||||
|
'highlightFaintText',
|
||||||
|
'highlightFaintLink',
|
||||||
|
'highlightPostFaintLink',
|
||||||
|
'highlightText',
|
||||||
|
'highlightLink',
|
||||||
|
'highlightIcon',
|
||||||
|
|
||||||
|
'popover',
|
||||||
|
'popoverLightText',
|
||||||
|
'popoverPostLink',
|
||||||
|
'popoverFaintText',
|
||||||
|
'popoverFaintLink',
|
||||||
|
'popoverPostFaintLink',
|
||||||
|
'popoverText',
|
||||||
|
'popoverLink',
|
||||||
|
'popoverIcon',
|
||||||
|
|
||||||
|
'selectedPost',
|
||||||
|
'selectedPostFaintText',
|
||||||
|
'selectedPostLightText',
|
||||||
|
'selectedPostPostLink',
|
||||||
|
'selectedPostFaintLink',
|
||||||
|
'selectedPostText',
|
||||||
|
'selectedPostLink',
|
||||||
|
'selectedPostIcon',
|
||||||
|
|
||||||
|
'selectedMenu',
|
||||||
|
'selectedMenuLightText',
|
||||||
|
'selectedMenuFaintText',
|
||||||
|
'selectedMenuFaintLink',
|
||||||
|
'selectedMenuText',
|
||||||
|
'selectedMenuLink',
|
||||||
|
'selectedMenuIcon',
|
||||||
|
|
||||||
|
'selectedMenuPopover',
|
||||||
|
'selectedMenuPopoverLightText',
|
||||||
|
'selectedMenuPopoverFaintText',
|
||||||
|
'selectedMenuPopoverFaintLink',
|
||||||
|
'selectedMenuPopoverText',
|
||||||
|
'selectedMenuPopoverLink',
|
||||||
|
'selectedMenuPopoverIcon',
|
||||||
|
|
||||||
|
'lightText',
|
||||||
|
|
||||||
|
'postLink',
|
||||||
|
|
||||||
|
'postGreentext',
|
||||||
|
|
||||||
|
'postCyantext',
|
||||||
|
|
||||||
|
'border',
|
||||||
|
|
||||||
|
'poll',
|
||||||
|
'pollText',
|
||||||
|
|
||||||
|
'icon',
|
||||||
|
|
||||||
|
// Foreground,
|
||||||
|
'fgText',
|
||||||
|
'fgLink',
|
||||||
|
|
||||||
|
// Panel header,
|
||||||
|
'panel',
|
||||||
|
'panelText',
|
||||||
|
'panelFaint',
|
||||||
|
'panelLink',
|
||||||
|
|
||||||
|
// Top bar,
|
||||||
|
'topBar',
|
||||||
|
'topBarLink',
|
||||||
|
|
||||||
|
// Tabs,
|
||||||
|
'tab',
|
||||||
|
'tabText',
|
||||||
|
'tabActiveText',
|
||||||
|
|
||||||
|
// Buttons,
|
||||||
|
'btn',
|
||||||
|
'btnText',
|
||||||
|
'btnPanelText',
|
||||||
|
'btnTopBarText',
|
||||||
|
|
||||||
|
// Buttons: pressed,
|
||||||
|
'btnPressed',
|
||||||
|
'btnPressedText',
|
||||||
|
'btnPressedPanel',
|
||||||
|
'btnPressedPanelText',
|
||||||
|
'btnPressedTopBar',
|
||||||
|
'btnPressedTopBarText',
|
||||||
|
|
||||||
|
// Buttons: toggled,
|
||||||
|
'btnToggled',
|
||||||
|
'btnToggledText',
|
||||||
|
'btnToggledPanelText',
|
||||||
|
'btnToggledTopBarText',
|
||||||
|
|
||||||
|
// Buttons: disabled,
|
||||||
|
'btnDisabled',
|
||||||
|
'btnDisabledText',
|
||||||
|
'btnDisabledPanelText',
|
||||||
|
'btnDisabledTopBarText',
|
||||||
|
|
||||||
|
// Input fields,
|
||||||
|
'input',
|
||||||
|
'inputText',
|
||||||
|
'inputPanelText',
|
||||||
|
'inputTopbarText',
|
||||||
|
|
||||||
|
'alertError',
|
||||||
|
'alertErrorText',
|
||||||
|
'alertErrorPanelText',
|
||||||
|
|
||||||
|
'alertWarning',
|
||||||
|
'alertWarningText',
|
||||||
|
'alertWarningPanelText',
|
||||||
|
|
||||||
|
'alertSuccess',
|
||||||
|
'alertSuccessText',
|
||||||
|
'alertSuccessPanelText',
|
||||||
|
|
||||||
|
'alertNeutral',
|
||||||
|
'alertNeutralText',
|
||||||
|
'alertNeutralPanelText',
|
||||||
|
|
||||||
|
'alertPopupError',
|
||||||
|
'alertPopupErrorText',
|
||||||
|
|
||||||
|
'alertPopupWarning',
|
||||||
|
'alertPopupWarningText',
|
||||||
|
|
||||||
|
'alertPopupSuccess',
|
||||||
|
'alertPopupSuccessText',
|
||||||
|
|
||||||
|
'alertPopupNeutral',
|
||||||
|
'alertPopupNeutralText',
|
||||||
|
|
||||||
|
'badgeNotification',
|
||||||
|
'badgeNotificationText',
|
||||||
|
|
||||||
|
'badgeNeutral',
|
||||||
|
'badgeNeutralText',
|
||||||
|
|
||||||
|
'chatBg',
|
||||||
|
|
||||||
|
'chatMessageIncomingBg',
|
||||||
|
'chatMessageIncomingText',
|
||||||
|
'chatMessageIncomingLink',
|
||||||
|
'chatMessageIncomingBorder',
|
||||||
|
'chatMessageOutgoingBg',
|
||||||
|
'chatMessageOutgoingText',
|
||||||
|
'chatMessageOutgoingLink',
|
||||||
|
'chatMessageOutgoingBorder'
|
||||||
|
]
|
|
@ -0,0 +1,58 @@
|
||||||
|
import allKeys from './theme2_keys'
|
||||||
|
|
||||||
|
// keys that are meant to be used globally, i.e. what's the rest of the theme is based upon.
|
||||||
|
const basePaletteKeys = new Set([
|
||||||
|
'bg',
|
||||||
|
'fg',
|
||||||
|
'text',
|
||||||
|
'link',
|
||||||
|
'accent',
|
||||||
|
|
||||||
|
'cBlue',
|
||||||
|
'cRed',
|
||||||
|
'cGreen',
|
||||||
|
'cOrange'
|
||||||
|
])
|
||||||
|
|
||||||
|
// Keys that are not available in editor and never meant to be edited
|
||||||
|
const hiddenKeys = new Set([
|
||||||
|
'profileBg',
|
||||||
|
'profileTint'
|
||||||
|
])
|
||||||
|
|
||||||
|
const extendedBasePrefixes = [
|
||||||
|
'border',
|
||||||
|
'icon',
|
||||||
|
'highlight',
|
||||||
|
'lightText',
|
||||||
|
|
||||||
|
'popover',
|
||||||
|
|
||||||
|
'panel',
|
||||||
|
'topBar',
|
||||||
|
'tab',
|
||||||
|
'btn',
|
||||||
|
'input',
|
||||||
|
'selectedMenu',
|
||||||
|
|
||||||
|
'alert',
|
||||||
|
'badge',
|
||||||
|
|
||||||
|
'post',
|
||||||
|
'selectedPost', // wrong nomenclature
|
||||||
|
'poll',
|
||||||
|
|
||||||
|
'chatBg',
|
||||||
|
'chatMessageIncoming',
|
||||||
|
'chatMessageOutgoing'
|
||||||
|
]
|
||||||
|
|
||||||
|
const extendedBaseKeys = Object.fromEntries(extendedBasePrefixes.map(prefix => [prefix, allKeys.filter(k => k.startsWith(prefix))]))
|
||||||
|
|
||||||
|
// Keysets that are only really used intermideately, i.e. to generate other colors
|
||||||
|
const temporary = new Set([
|
||||||
|
'border',
|
||||||
|
'highlight'
|
||||||
|
])
|
||||||
|
|
||||||
|
const temporaryColors = {}
|
|
@ -1,24 +1,89 @@
|
||||||
import { convert, brightness } from 'chromatism'
|
import { convert, brightness } from 'chromatism'
|
||||||
import merge from 'lodash.merge'
|
import merge from 'lodash.merge'
|
||||||
import { alphaBlend, getTextColor, rgba2css, mixrgb, relativeLuminance } from '../color_convert/color_convert.js'
|
import {
|
||||||
|
alphaBlend,
|
||||||
|
getTextColor,
|
||||||
|
rgba2css,
|
||||||
|
mixrgb,
|
||||||
|
relativeLuminance
|
||||||
|
} from '../color_convert/color_convert.js'
|
||||||
|
|
||||||
|
import Root from 'src/components/root.style.js'
|
||||||
|
import TopBar from 'src/components/top_bar.style.js'
|
||||||
import Underlay from 'src/components/underlay.style.js'
|
import Underlay from 'src/components/underlay.style.js'
|
||||||
|
import Popover from 'src/components/popover.style.js'
|
||||||
|
import DropdownMenu from 'src/components/dropdown_menu.style.js'
|
||||||
import Panel from 'src/components/panel.style.js'
|
import Panel from 'src/components/panel.style.js'
|
||||||
import PanelHeader from 'src/components/panel_header.style.js'
|
import PanelHeader from 'src/components/panel_header.style.js'
|
||||||
import Button from 'src/components/button.style.js'
|
import Button from 'src/components/button.style.js'
|
||||||
|
import Input from 'src/components/input.style.js'
|
||||||
import Text from 'src/components/text.style.js'
|
import Text from 'src/components/text.style.js'
|
||||||
import Link from 'src/components/link.style.js'
|
import Link from 'src/components/link.style.js'
|
||||||
import Icon from 'src/components/icon.style.js'
|
import Icon from 'src/components/icon.style.js'
|
||||||
|
|
||||||
const root = Underlay
|
export const DEFAULT_SHADOWS = {
|
||||||
|
panel: [{
|
||||||
|
x: 1,
|
||||||
|
y: 1,
|
||||||
|
blur: 4,
|
||||||
|
spread: 0,
|
||||||
|
color: '#000000',
|
||||||
|
alpha: 0.6
|
||||||
|
}],
|
||||||
|
topBar: [{
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
blur: 4,
|
||||||
|
spread: 0,
|
||||||
|
color: '#000000',
|
||||||
|
alpha: 0.6
|
||||||
|
}],
|
||||||
|
popup: [{
|
||||||
|
x: 2,
|
||||||
|
y: 2,
|
||||||
|
blur: 3,
|
||||||
|
spread: 0,
|
||||||
|
color: '#000000',
|
||||||
|
alpha: 0.5
|
||||||
|
}],
|
||||||
|
avatar: [{
|
||||||
|
x: 0,
|
||||||
|
y: 1,
|
||||||
|
blur: 8,
|
||||||
|
spread: 0,
|
||||||
|
color: '#000000',
|
||||||
|
alpha: 0.7
|
||||||
|
}],
|
||||||
|
avatarStatus: [],
|
||||||
|
panelHeader: []
|
||||||
|
}
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
Underlay,
|
Root,
|
||||||
Panel,
|
|
||||||
PanelHeader,
|
|
||||||
Button,
|
|
||||||
Text,
|
Text,
|
||||||
Link,
|
Link,
|
||||||
Icon
|
Icon,
|
||||||
|
Underlay,
|
||||||
|
Popover,
|
||||||
|
DropdownMenu,
|
||||||
|
Panel,
|
||||||
|
PanelHeader,
|
||||||
|
TopBar,
|
||||||
|
Button,
|
||||||
|
Input
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Unrolls" a tree structure of item: { parent: { ...item2, parent: { ...item3, parent: {...} } }}
|
||||||
|
// into an array [item2, item3] for iterating
|
||||||
|
const unroll = (item) => {
|
||||||
|
const out = []
|
||||||
|
let currentParent = item.parent
|
||||||
|
while (currentParent) {
|
||||||
|
const { parent: newParent, ...rest } = currentParent
|
||||||
|
out.push(rest)
|
||||||
|
currentParent = newParent
|
||||||
|
}
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// This gives you an array of arrays of all possible unique (i.e. order-insensitive) combinations
|
// This gives you an array of arrays of all possible unique (i.e. order-insensitive) combinations
|
||||||
|
@ -38,7 +103,9 @@ export const getAllPossibleCombinations = (array) => {
|
||||||
return combos.reduce((acc, x) => [...acc, ...x], [])
|
return combos.reduce((acc, x) => [...acc, ...x], [])
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ruleToSelector = (rule, isParent) => {
|
// Converts rule, parents and their criteria into a CSS (or path if ignoreOutOfTreeSelector == true) selector
|
||||||
|
export const ruleToSelector = (rule, ignoreOutOfTreeSelector, isParent) => {
|
||||||
|
if (!rule && !isParent) return null
|
||||||
const component = components[rule.component]
|
const component = components[rule.component]
|
||||||
const { states, variants, selector, outOfTreeSelector } = component
|
const { states, variants, selector, outOfTreeSelector } = component
|
||||||
|
|
||||||
|
@ -51,10 +118,12 @@ export const ruleToSelector = (rule, isParent) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let realSelector
|
let realSelector
|
||||||
if (isParent) {
|
if (selector === ':root') {
|
||||||
|
realSelector = ''
|
||||||
|
} else if (isParent) {
|
||||||
realSelector = selector
|
realSelector = selector
|
||||||
} else {
|
} else {
|
||||||
if (outOfTreeSelector) realSelector = outOfTreeSelector
|
if (outOfTreeSelector && !ignoreOutOfTreeSelector) realSelector = outOfTreeSelector
|
||||||
else realSelector = selector
|
else realSelector = selector
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,123 +136,133 @@ export const ruleToSelector = (rule, isParent) => {
|
||||||
.join('')
|
.join('')
|
||||||
|
|
||||||
if (rule.parent) {
|
if (rule.parent) {
|
||||||
return ruleToSelector(rule.parent, true) + ' ' + selectors
|
return (ruleToSelector(rule.parent, ignoreOutOfTreeSelector, true) + ' ' + selectors).trim()
|
||||||
}
|
}
|
||||||
return selectors
|
return selectors.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
const combinationsMatch = (criteria, subject) => {
|
||||||
|
if (criteria.component !== subject.component) return false
|
||||||
|
|
||||||
|
// All variants inherit from normal
|
||||||
|
const subjectVariant = Object.prototype.hasOwnProperty.call(subject, 'variant') ? subject.variant : 'normal'
|
||||||
|
if (subjectVariant !== 'normal') {
|
||||||
|
if (criteria.variant !== subject.variant) return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const subjectStatesSet = new Set(['normal', ...(subject.state || [])])
|
||||||
|
const criteriaStatesSet = new Set(['normal', ...(criteria.state || [])])
|
||||||
|
|
||||||
|
// Subject states > 1 essentially means state is "normal" and therefore matches
|
||||||
|
if (subjectStatesSet.size > 1) {
|
||||||
|
const setsAreEqual =
|
||||||
|
[...criteriaStatesSet].every(state => subjectStatesSet.has(state)) &&
|
||||||
|
[...subjectStatesSet].every(state => criteriaStatesSet.has(state))
|
||||||
|
|
||||||
|
if (!setsAreEqual) return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const findRules = criteria => subject => {
|
||||||
|
// If we searching for "general" rules - ignore "specific" ones
|
||||||
|
if (criteria.parent === null && !!subject.parent) return false
|
||||||
|
if (!combinationsMatch(criteria, subject)) return false
|
||||||
|
|
||||||
|
if (criteria.parent !== undefined && criteria.parent !== null) {
|
||||||
|
if (!subject.parent) return true
|
||||||
|
const pathCriteria = unroll(criteria)
|
||||||
|
const pathSubject = unroll(subject)
|
||||||
|
if (pathCriteria.length < pathSubject.length) return false
|
||||||
|
|
||||||
|
// Search: .a .b .c
|
||||||
|
// Matches: .a .b .c; .b .c; .c; .z .a .b .c
|
||||||
|
// Does not match .a .b .c .d, .a .b .e
|
||||||
|
for (let i = 0; i < pathCriteria.length; i++) {
|
||||||
|
const criteriaParent = pathCriteria[i]
|
||||||
|
const subjectParent = pathSubject[i]
|
||||||
|
if (!subjectParent) return true
|
||||||
|
if (!combinationsMatch(criteriaParent, subjectParent)) return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
export const init = (extraRuleset, palette) => {
|
export const init = (extraRuleset, palette) => {
|
||||||
const rootName = root.name
|
const cache = {}
|
||||||
|
const computed = {}
|
||||||
|
|
||||||
const rules = []
|
const rules = []
|
||||||
const rulesByComponent = {}
|
|
||||||
|
|
||||||
const ruleset = [
|
const ruleset = [
|
||||||
...Object.values(components).map(c => c.defaultRules || []).reduce((acc, arr) => [...acc, ...arr], []),
|
...Object.values(components).map(c => c.defaultRules.map(r => ({ component: c.name, ...r })) || []).reduce((acc, arr) => [...acc, ...arr], []),
|
||||||
...extraRuleset
|
...extraRuleset
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const virtualComponents = new Set(Object.values(components).filter(c => c.virtual).map(c => c.name))
|
||||||
|
|
||||||
const addRule = (rule) => {
|
const addRule = (rule) => {
|
||||||
rules.push(rule)
|
rules.push(rule)
|
||||||
rulesByComponent[rule.component] = rulesByComponent[rule.component] || []
|
|
||||||
rulesByComponent[rule.component].push(rule)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const findRules = (searchCombination, parent) => rule => {
|
const findColor = (color, inheritedBackground, lowerLevelBackground) => {
|
||||||
// inexact search
|
if (typeof color !== 'string' || (!color.startsWith('--') && !color.startsWith('$'))) return color
|
||||||
const doesCombinationMatch = () => {
|
|
||||||
if (searchCombination.component !== rule.component) return false
|
|
||||||
const ruleVariant = Object.prototype.hasOwnProperty.call(rule, 'variant') ? rule.variant : 'normal'
|
|
||||||
|
|
||||||
if (ruleVariant !== 'normal') {
|
|
||||||
if (searchCombination.variant !== rule.variant) return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const ruleHasStateDefined = Object.prototype.hasOwnProperty.call(rule, 'state')
|
|
||||||
let ruleStateSet
|
|
||||||
if (ruleHasStateDefined) {
|
|
||||||
ruleStateSet = new Set(['normal', ...rule.state])
|
|
||||||
} else {
|
|
||||||
ruleStateSet = new Set(['normal'])
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ruleStateSet.size > 1) {
|
|
||||||
const ruleStatesSet = ruleStateSet
|
|
||||||
const combinationSet = new Set(['normal', ...searchCombination.state])
|
|
||||||
const setsAreEqual = searchCombination.state.every(state => ruleStatesSet.has(state)) &&
|
|
||||||
[...ruleStatesSet].every(state => combinationSet.has(state))
|
|
||||||
return setsAreEqual
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const combinationMatches = doesCombinationMatch()
|
|
||||||
if (!parent || !combinationMatches) return combinationMatches
|
|
||||||
|
|
||||||
// exact search
|
|
||||||
|
|
||||||
// unroll parents into array
|
|
||||||
const unroll = (item) => {
|
|
||||||
const out = []
|
|
||||||
let currentParent = item.parent
|
|
||||||
while (currentParent) {
|
|
||||||
const { parent: newParent, ...rest } = currentParent
|
|
||||||
out.push(rest)
|
|
||||||
currentParent = newParent
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
const { parent: _, ...rest } = parent
|
|
||||||
const pathSearch = [rest, ...unroll(parent)]
|
|
||||||
const pathRule = unroll(rule)
|
|
||||||
if (pathSearch.length !== pathRule.length) return false
|
|
||||||
const pathsMatch = pathSearch.every((searchRule, i) => {
|
|
||||||
const existingRule = pathRule[i]
|
|
||||||
if (existingRule.component !== searchRule.component) return false
|
|
||||||
if (existingRule.variant !== searchRule.variant) return false
|
|
||||||
const existingRuleStatesSet = new Set(['normal', ...(existingRule.state || [])])
|
|
||||||
const searchStatesSet = new Set(['normal', ...(searchRule.state || [])])
|
|
||||||
const setsAreEqual = existingRule.state.every(state => searchStatesSet.has(state)) &&
|
|
||||||
[...searchStatesSet].every(state => existingRuleStatesSet.has(state))
|
|
||||||
return setsAreEqual
|
|
||||||
})
|
|
||||||
return pathsMatch
|
|
||||||
}
|
|
||||||
|
|
||||||
const findLowerLevelRule = (parent, filter = () => true) => {
|
|
||||||
let lowerLevelComponent = null
|
|
||||||
let currentParent = parent
|
|
||||||
while (currentParent) {
|
|
||||||
const rulesParent = ruleset.filter(findRules(currentParent))
|
|
||||||
rulesParent > 1 && console.warn('OOPS')
|
|
||||||
lowerLevelComponent = rulesParent[rulesParent.length - 1]
|
|
||||||
currentParent = currentParent.parent
|
|
||||||
if (lowerLevelComponent && filter(lowerLevelComponent)) currentParent = null
|
|
||||||
}
|
|
||||||
return filter(lowerLevelComponent) ? lowerLevelComponent : null
|
|
||||||
}
|
|
||||||
|
|
||||||
const findColor = (color, background) => {
|
|
||||||
if (typeof color !== 'string' || !color.startsWith('--')) return color
|
|
||||||
let targetColor = null
|
let targetColor = null
|
||||||
// Color references other color
|
if (color.startsWith('--')) {
|
||||||
const [variable, modifier] = color.split(/,/g).map(str => str.trim())
|
const [variable, modifier] = color.split(/,/g).map(str => str.trim())
|
||||||
const variableSlot = variable.substring(2)
|
const variableSlot = variable.substring(2)
|
||||||
|
if (variableSlot.startsWith('parent')) {
|
||||||
|
// TODO support more than just background?
|
||||||
|
if (variableSlot === 'parent') {
|
||||||
|
targetColor = lowerLevelBackground
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (variableSlot) {
|
||||||
|
case 'background':
|
||||||
|
targetColor = inheritedBackground
|
||||||
|
break
|
||||||
|
default:
|
||||||
targetColor = palette[variableSlot]
|
targetColor = palette[variableSlot]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (modifier) {
|
if (modifier) {
|
||||||
const effectiveBackground = background ?? targetColor
|
const effectiveBackground = lowerLevelBackground ?? targetColor
|
||||||
const isLightOnDark = relativeLuminance(convert(effectiveBackground).rgb) < 0.5
|
const isLightOnDark = relativeLuminance(convert(effectiveBackground).rgb) < 0.5
|
||||||
const mod = isLightOnDark ? 1 : -1
|
const mod = isLightOnDark ? 1 : -1
|
||||||
targetColor = brightness(Number.parseFloat(modifier) * mod, targetColor).rgb
|
targetColor = brightness(Number.parseFloat(modifier) * mod, targetColor).rgb
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (color.startsWith('$')) {
|
||||||
|
try {
|
||||||
|
const { funcName, argsString } = /\$(?<funcName>\w+)\((?<argsString>[a-zA-Z0-9-,.'"\s]*)\)/.exec(color).groups
|
||||||
|
const args = argsString.split(/,/g).map(a => a.trim())
|
||||||
|
switch (funcName) {
|
||||||
|
case 'blend': {
|
||||||
|
if (args.length !== 3) {
|
||||||
|
throw new Error(`$blend requires 3 arguments, ${args.length} were provided`)
|
||||||
|
}
|
||||||
|
const backgroundArg = findColor(args[2], inheritedBackground, lowerLevelBackground)
|
||||||
|
const foregroundArg = findColor(args[0], inheritedBackground, lowerLevelBackground)
|
||||||
|
const amount = Number(args[1])
|
||||||
|
targetColor = alphaBlend(backgroundArg, amount, foregroundArg)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failure executing color function', e)
|
||||||
|
targetColor = '#FF00FF'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Color references other color
|
||||||
return targetColor
|
return targetColor
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTextColorAlpha = (rule, lowerRule, value) => {
|
const cssColorString = (color, alpha) => rgba2css({ ...convert(color).rgb, a: alpha })
|
||||||
|
|
||||||
|
const getTextColorAlpha = (rule, lowerColor, value) => {
|
||||||
const opacity = rule.directives.textOpacity
|
const opacity = rule.directives.textOpacity
|
||||||
const backgroundColor = convert(lowerRule.cache.background).rgb
|
const backgroundColor = convert(lowerColor).rgb
|
||||||
const textColor = convert(findColor(value, backgroundColor)).rgb
|
const textColor = convert(findColor(value, backgroundColor)).rgb
|
||||||
if (opacity === null || opacity === undefined || opacity >= 1) {
|
if (opacity === null || opacity === undefined || opacity >= 1) {
|
||||||
return convert(textColor).hex
|
return convert(textColor).hex
|
||||||
|
@ -202,6 +281,44 @@ export const init = (extraRuleset, palette) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getCssShadow = (input, usesDropShadow) => {
|
||||||
|
if (input.length === 0) {
|
||||||
|
return 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
return input
|
||||||
|
.filter(_ => usesDropShadow ? _.inset : _)
|
||||||
|
.map((shad) => [
|
||||||
|
shad.x,
|
||||||
|
shad.y,
|
||||||
|
shad.blur,
|
||||||
|
shad.spread
|
||||||
|
].map(_ => _ + 'px ').concat([
|
||||||
|
cssColorString(findColor(shad.color), shad.alpha),
|
||||||
|
shad.inset ? 'inset' : ''
|
||||||
|
]).join(' ')).join(', ')
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCssShadowFilter = (input) => {
|
||||||
|
if (input.length === 0) {
|
||||||
|
return 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
return input
|
||||||
|
// drop-shadow doesn't support inset or spread
|
||||||
|
.filter((shad) => !shad.inset && Number(shad.spread) === 0)
|
||||||
|
.map((shad) => [
|
||||||
|
shad.x,
|
||||||
|
shad.y,
|
||||||
|
// drop-shadow's blur is twice as strong compared to box-shadow
|
||||||
|
shad.blur / 2
|
||||||
|
].map(_ => _ + 'px').concat([
|
||||||
|
cssColorString(findColor(shad.color), shad.alpha)
|
||||||
|
]).join(' '))
|
||||||
|
.map(_ => `drop-shadow(${_})`)
|
||||||
|
.join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
const processInnerComponent = (component, parent) => {
|
const processInnerComponent = (component, parent) => {
|
||||||
const {
|
const {
|
||||||
validInnerComponents = [],
|
validInnerComponents = [],
|
||||||
|
@ -210,6 +327,7 @@ export const init = (extraRuleset, palette) => {
|
||||||
name
|
name
|
||||||
} = component
|
} = component
|
||||||
|
|
||||||
|
// Normalizing states and variants to always include "normal"
|
||||||
const states = { normal: '', ...originalStates }
|
const states = { normal: '', ...originalStates }
|
||||||
const variants = { normal: '', ...originalVariants }
|
const variants = { normal: '', ...originalVariants }
|
||||||
const innerComponents = validInnerComponents.map(name => components[name])
|
const innerComponents = validInnerComponents.map(name => components[name])
|
||||||
|
@ -219,13 +337,24 @@ export const init = (extraRuleset, palette) => {
|
||||||
return stateCombinations.map(state => ({ variant, state }))
|
return stateCombinations.map(state => ({ variant, state }))
|
||||||
}).reduce((acc, x) => [...acc, ...x], [])
|
}).reduce((acc, x) => [...acc, ...x], [])
|
||||||
|
|
||||||
const VIRTUAL_COMPONENTS = new Set(['Text', 'Link', 'Icon'])
|
|
||||||
|
|
||||||
stateVariantCombination.forEach(combination => {
|
stateVariantCombination.forEach(combination => {
|
||||||
let needRuleAdd = false
|
const soloSelector = ruleToSelector({ component: component.name, ...combination }, true)
|
||||||
|
const selector = ruleToSelector({ component: component.name, ...combination, parent }, true)
|
||||||
|
|
||||||
if (VIRTUAL_COMPONENTS.has(component.name)) {
|
// Inheriting all of the applicable rules
|
||||||
const selector = component.name + ruleToSelector({ component: component.name, ...combination })
|
const existingRules = ruleset.filter(findRules({ component: component.name, ...combination, parent }))
|
||||||
|
const { directives: computedDirectives } = existingRules.reduce((acc, rule) => merge(acc, rule), {})
|
||||||
|
const computedRule = {
|
||||||
|
component: component.name,
|
||||||
|
...combination,
|
||||||
|
parent,
|
||||||
|
directives: computedDirectives
|
||||||
|
}
|
||||||
|
|
||||||
|
computed[selector] = computed[selector] || {}
|
||||||
|
computed[selector].computedRule = computedRule
|
||||||
|
|
||||||
|
if (virtualComponents.has(component.name)) {
|
||||||
const virtualName = [
|
const virtualName = [
|
||||||
'--',
|
'--',
|
||||||
component.name.toLowerCase(),
|
component.name.toLowerCase(),
|
||||||
|
@ -235,52 +364,46 @@ export const init = (extraRuleset, palette) => {
|
||||||
...combination.state.filter(x => x !== 'normal').toSorted().map(state => state[0].toUpperCase() + state.slice(1).toLowerCase())
|
...combination.state.filter(x => x !== 'normal').toSorted().map(state => state[0].toUpperCase() + state.slice(1).toLowerCase())
|
||||||
].join('')
|
].join('')
|
||||||
|
|
||||||
const lowerLevel = findLowerLevelRule(parent, (r) => {
|
let inheritedTextColor = computedDirectives.textColor
|
||||||
if (!r) return false
|
let inheritedTextOpacity = computedDirectives.textOpacity
|
||||||
if (components[r.component].validInnerComponents.indexOf(component.name) < 0) return false
|
let inheritedTextOpacityMode = computedDirectives.textOpacityMode
|
||||||
if (r.cache.background === undefined) return false
|
const lowerLevelTextSelector = [...selector.split(/ /g).slice(0, -1), soloSelector].join(' ')
|
||||||
if (r.cache.textDefined) {
|
const lowerLevelTextRule = computed[lowerLevelTextSelector]
|
||||||
return !r.cache.textDefined[selector]
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!lowerLevel) return
|
if (inheritedTextColor == null || inheritedTextOpacity == null || inheritedTextOpacityMode == null) {
|
||||||
|
inheritedTextColor = computedDirectives.textColor ?? lowerLevelTextRule.textColor
|
||||||
let inheritedTextColorRule
|
inheritedTextOpacity = computedDirectives.textOpacity ?? lowerLevelTextRule.textOpacity
|
||||||
const inheritedTextColorRules = findLowerLevelRule(parent, (r) => {
|
inheritedTextOpacityMode = computedDirectives.textOpacityMode ?? lowerLevelTextRule.textOpacityMode
|
||||||
return r.cache?.textDefined?.[selector]
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!inheritedTextColorRule) {
|
|
||||||
const generalTextColorRules = ruleset.filter(findRules({ component: component.name, ...combination }, null, true))
|
|
||||||
inheritedTextColorRule = generalTextColorRules.reduce((acc, rule) => merge(acc, rule), {})
|
|
||||||
} else {
|
|
||||||
inheritedTextColorRule = inheritedTextColorRules.reduce((acc, rule) => merge(acc, rule), {})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let inheritedTextColor
|
const newTextRule = {
|
||||||
let inheritedTextOpacity = {}
|
...computedRule,
|
||||||
if (inheritedTextColorRule) {
|
directives: {
|
||||||
inheritedTextColor = findColor(inheritedTextColorRule.directives.textColor, convert(lowerLevel.cache.background).rgb)
|
...computedRule.directives,
|
||||||
// also inherit opacity settings
|
textColor: inheritedTextColor,
|
||||||
const { textOpacity, textOpacityMode } = inheritedTextColorRule.directives
|
textOpacity: inheritedTextOpacity,
|
||||||
inheritedTextOpacity = { textOpacity, textOpacityMode }
|
textOpacityMode: inheritedTextOpacityMode
|
||||||
} else {
|
|
||||||
// Emergency fallback
|
|
||||||
inheritedTextColor = '#000000'
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const lowerLevelSelector = selector.split(/ /g).slice(0, -1).join(' ')
|
||||||
|
const lowerLevelBackground = cache[lowerLevelSelector].background
|
||||||
|
|
||||||
const textColor = getTextColor(
|
const textColor = getTextColor(
|
||||||
convert(lowerLevel.cache.background).rgb,
|
convert(lowerLevelBackground).rgb,
|
||||||
convert(inheritedTextColor).rgb,
|
// TODO properly provide "parent" text color?
|
||||||
component.name === 'Link' // make it configurable?
|
convert(findColor(inheritedTextColor, null, lowerLevelBackground)).rgb,
|
||||||
|
true // component.name === 'Link' || combination.variant === 'greentext' // make it configurable?
|
||||||
)
|
)
|
||||||
|
|
||||||
lowerLevel.cache.textDefined = lowerLevel.cache.textDefined || {}
|
// Storing color data in lower layer to use as custom css properties
|
||||||
lowerLevel.cache.textDefined[selector] = textColor
|
cache[lowerLevelSelector].textDefined = cache[lowerLevelSelector].textDefined || {}
|
||||||
lowerLevel.virtualDirectives = lowerLevel.virtualDirectives || {}
|
cache[lowerLevelSelector].textDefined[selector] = textColor
|
||||||
lowerLevel.virtualDirectives[virtualName] = getTextColorAlpha(inheritedTextColorRule, lowerLevel, textColor)
|
|
||||||
|
const virtualDirectives = {}
|
||||||
|
virtualDirectives[virtualName] = getTextColorAlpha(newTextRule, lowerLevelBackground, textColor)
|
||||||
|
|
||||||
|
// lastRule.computed = lastRule.computed || {}
|
||||||
|
|
||||||
const directives = {
|
const directives = {
|
||||||
textColor,
|
textColor,
|
||||||
|
@ -288,45 +411,54 @@ export const init = (extraRuleset, palette) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug: lets you see what it think background color should be
|
// Debug: lets you see what it think background color should be
|
||||||
directives.background = convert(lowerLevel.cache.background).hex
|
// directives.background = convert(cache[lowerLevelSelector].background).hex
|
||||||
|
|
||||||
addRule({
|
addRule({
|
||||||
parent,
|
parent,
|
||||||
virtual: true,
|
virtual: true,
|
||||||
component: component.name,
|
component: component.name,
|
||||||
...combination,
|
...combination,
|
||||||
cache: { background: lowerLevel.cache.background },
|
directives,
|
||||||
directives
|
virtualDirectives
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
const existingGlobalRules = ruleset.filter(findRules({ component: component.name, ...combination }, null))
|
cache[selector] = cache[selector] || {}
|
||||||
const existingRules = ruleset.filter(findRules({ component: component.name, ...combination }, parent))
|
computed[selector] = computed[selector] || {}
|
||||||
|
|
||||||
// Global (general) rules
|
if (computedDirectives.background) {
|
||||||
if (existingGlobalRules.length !== 0) {
|
let inheritRule = null
|
||||||
const totalRule = existingGlobalRules.reduce((acc, rule) => merge(acc, rule), {})
|
const variantRules = ruleset.filter(findRules({ component: component.name, variant: combination.variant, parent }))
|
||||||
const { directives } = totalRule
|
const lastVariantRule = variantRules[variantRules.length - 1]
|
||||||
|
if (lastVariantRule) {
|
||||||
|
inheritRule = lastVariantRule
|
||||||
|
} else {
|
||||||
|
const normalRules = ruleset.filter(findRules({ component: component.name, parent }))
|
||||||
|
const lastNormalRule = normalRules[normalRules.length - 1]
|
||||||
|
inheritRule = lastNormalRule
|
||||||
|
}
|
||||||
|
|
||||||
// last rule is used as a cache
|
const inheritSelector = ruleToSelector({ ...inheritRule, parent }, true)
|
||||||
const lastRule = existingGlobalRules[existingGlobalRules.length - 1]
|
const inheritedBackground = cache[inheritSelector].background
|
||||||
lastRule.cache = lastRule.cache || {}
|
const lowerLevelSelector = selector.split(/ /g).slice(0, -1).join(' ')
|
||||||
|
|
||||||
if (directives.background) {
|
|
||||||
const rgb = convert(findColor(directives.background)).rgb
|
|
||||||
|
|
||||||
// TODO: DEFAULT TEXT COLOR
|
// TODO: DEFAULT TEXT COLOR
|
||||||
const bg = findLowerLevelRule(parent)?.cache.background || convert('#FFFFFF').rgb
|
const bg = cache[lowerLevelSelector]?.background || convert('#FFFFFF').rgb
|
||||||
|
|
||||||
if (!lastRule.cache.background) {
|
console.log('SELECTOR', lowerLevelSelector)
|
||||||
const blend = directives.opacity < 1 ? alphaBlend(rgb, directives.opacity, bg) : rgb
|
|
||||||
lastRule.cache.background = blend
|
|
||||||
|
|
||||||
needRuleAdd = true
|
const rgb = convert(findColor(computedDirectives.background, inheritedBackground, cache[lowerLevelSelector].background)).rgb
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needRuleAdd) {
|
if (!cache[selector].background) {
|
||||||
addRule(lastRule)
|
const blend = computedDirectives.opacity < 1 ? alphaBlend(rgb, computedDirectives.opacity, bg) : rgb
|
||||||
|
cache[selector].background = blend
|
||||||
|
computed[selector].background = rgb
|
||||||
|
|
||||||
|
addRule({
|
||||||
|
component: component.name,
|
||||||
|
...combination,
|
||||||
|
parent,
|
||||||
|
directives: computedDirectives
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,12 +470,12 @@ export const init = (extraRuleset, palette) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
processInnerComponent(components[rootName])
|
processInnerComponent(components.Root, { component: 'Root' })
|
||||||
|
|
||||||
return {
|
return {
|
||||||
raw: rules,
|
raw: rules,
|
||||||
css: rules.map(rule => {
|
css: rules.map(rule => {
|
||||||
if (rule.virtual) return ''
|
// if (rule.virtual) return ''
|
||||||
|
|
||||||
let selector = ruleToSelector(rule).replace(/\/\*.*\*\//g, '')
|
let selector = ruleToSelector(rule).replace(/\/\*.*\*\//g, '')
|
||||||
if (!selector) {
|
if (!selector) {
|
||||||
|
@ -356,10 +488,23 @@ export const init = (extraRuleset, palette) => {
|
||||||
return ' ' + k + ': ' + v
|
return ' ' + k + ': ' + v
|
||||||
}).join(';\n')
|
}).join(';\n')
|
||||||
|
|
||||||
const directives = Object.entries(rule.directives).map(([k, v]) => {
|
let directives
|
||||||
|
if (rule.component !== 'Root') {
|
||||||
|
directives = Object.entries(rule.directives).map(([k, v]) => {
|
||||||
switch (k) {
|
switch (k) {
|
||||||
|
case 'shadow': {
|
||||||
|
return ' ' + [
|
||||||
|
'--shadow: ' + getCssShadow(v),
|
||||||
|
'--shadowFilter: ' + getCssShadowFilter(v),
|
||||||
|
'--shadowInset: ' + getCssShadow(v, true)
|
||||||
|
].join(';\n ')
|
||||||
|
}
|
||||||
case 'background': {
|
case 'background': {
|
||||||
return 'background-color: ' + rgba2css({ ...convert(findColor(v)).rgb, a: rule.directives.opacity ?? 1 })
|
const color = cssColorString(computed[ruleToSelector(rule, true)].background, rule.directives.opacity)
|
||||||
|
return [
|
||||||
|
'background-color: ' + color,
|
||||||
|
' --background: ' + color
|
||||||
|
].join(';\n')
|
||||||
}
|
}
|
||||||
case 'textColor': {
|
case 'textColor': {
|
||||||
return 'color: ' + v
|
return 'color: ' + v
|
||||||
|
@ -367,11 +512,14 @@ export const init = (extraRuleset, palette) => {
|
||||||
default: return ''
|
default: return ''
|
||||||
}
|
}
|
||||||
}).filter(x => x).map(x => ' ' + x).join(';\n')
|
}).filter(x => x).map(x => ' ' + x).join(';\n')
|
||||||
|
} else {
|
||||||
|
directives = {}
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
header,
|
header,
|
||||||
directives + ';',
|
directives + ';',
|
||||||
' color: var(--text);',
|
!rule.virtual ? ' color: var(--text);' : '',
|
||||||
'',
|
'',
|
||||||
virtualDirectives,
|
virtualDirectives,
|
||||||
footer
|
footer
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
export default {
|
||||||
|
name: 'TopBar',
|
||||||
|
selector: 'nav',
|
||||||
|
validInnerComponents: [
|
||||||
|
'Link',
|
||||||
|
'Text',
|
||||||
|
'Icon'
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue