better virtual components and stuff
This commit is contained in:
parent
ff2db7a247
commit
53a4b1f9a6
28
src/App.scss
28
src/App.scss
|
@ -24,8 +24,7 @@ body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
font-family: var(--interfaceFont, sans-serif);
|
font-family: var(--interfaceFont, sans-serif);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: $fallback--text;
|
color: var(--text);
|
||||||
color: var(--text, $fallback--text);
|
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
overscroll-behavior-y: none;
|
overscroll-behavior-y: none;
|
||||||
|
@ -111,8 +110,7 @@ body {
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: $fallback--link;
|
color: var(--link);
|
||||||
color: var(--link, $fallback--link);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
|
@ -128,8 +126,7 @@ h4 {
|
||||||
i[class*="icon-"],
|
i[class*="icon-"],
|
||||||
.svg-inline--fa,
|
.svg-inline--fa,
|
||||||
.iconLetter {
|
.iconLetter {
|
||||||
color: $fallback--icon;
|
color: var(--icon);
|
||||||
color: var(--icon, $fallback--icon);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-unstyled:hover,
|
.button-unstyled:hover,
|
||||||
|
@ -763,17 +760,11 @@ option {
|
||||||
}
|
}
|
||||||
|
|
||||||
.faint {
|
.faint {
|
||||||
color: $fallback--faint;
|
--text: var(--textFaint);
|
||||||
color: var(--faint, $fallback--faint);
|
--textGreentext: var(--textGreentextFaint);
|
||||||
}
|
--link: var(--linkFaint);
|
||||||
|
|
||||||
.faint-link {
|
color: var(--text);
|
||||||
color: $fallback--faint;
|
|
||||||
color: var(--faint, $fallback--faint);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.visibility-notice {
|
.visibility-notice {
|
||||||
|
@ -816,6 +807,11 @@ option {
|
||||||
opacity: 0.25;
|
opacity: 0.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.timeago {
|
||||||
|
--link: var(--text);
|
||||||
|
--linkFaint: var(--textFaint);
|
||||||
|
}
|
||||||
|
|
||||||
.login-hint {
|
.login-hint {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,15 @@
|
||||||
export default {
|
export default {
|
||||||
name: 'Icon',
|
name: 'Icon',
|
||||||
selector: '.icon'
|
virtual: true,
|
||||||
|
selector: '.svg-inline--fa',
|
||||||
|
defaultRules: [
|
||||||
|
{
|
||||||
|
component: 'Icon',
|
||||||
|
directives: {
|
||||||
|
textColor: '--text',
|
||||||
|
textOpacity: 0.5,
|
||||||
|
textOpacityMode: 'mixrgb'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
export default {
|
||||||
|
name: 'Link',
|
||||||
|
selector: 'a',
|
||||||
|
virtual: true,
|
||||||
|
states: {
|
||||||
|
faint: '.faint'
|
||||||
|
},
|
||||||
|
defaultRules: [
|
||||||
|
{
|
||||||
|
component: 'Link',
|
||||||
|
directives: {
|
||||||
|
textColor: '--link'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Link',
|
||||||
|
state: ['faint'],
|
||||||
|
directives: {
|
||||||
|
textColor: '--link',
|
||||||
|
textOpacity: 0.5,
|
||||||
|
textOpacityMode: 'fake'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -155,7 +155,7 @@
|
||||||
<router-link
|
<router-link
|
||||||
v-if="notification.status"
|
v-if="notification.status"
|
||||||
:to="{ name: 'conversation', params: { id: notification.status.id } }"
|
:to="{ name: 'conversation', params: { id: notification.status.id } }"
|
||||||
class="timeago-link faint-link"
|
class="timeago-link faint"
|
||||||
>
|
>
|
||||||
<Timeago
|
<Timeago
|
||||||
:time="notification.created_at"
|
:time="notification.created_at"
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.loadmore-error {
|
.loadmore-error {
|
||||||
color: $fallback--text;
|
color: var(--text);
|
||||||
color: var(--text, $fallback--text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification {
|
.notification {
|
||||||
|
@ -25,7 +24,7 @@
|
||||||
|
|
||||||
&.unseen {
|
&.unseen {
|
||||||
.notification-overlay {
|
.notification-overlay {
|
||||||
background-image: linear-gradient(135deg, var(--badgeNotification, $fallback--cRed) 4px, transparent 10px);
|
background-image: linear-gradient(135deg, var(--badgeNotification) 4px, transparent 10px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,24 +59,17 @@
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.faint {
|
|
||||||
--link: var(--faintLink);
|
|
||||||
--text: var(--faint);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.follow-request-accept {
|
.follow-request-accept {
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $fallback--text;
|
color: var(--text);
|
||||||
color: var(--text, $fallback--text);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.follow-request-reject {
|
.follow-request-reject {
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $fallback--cRed;
|
color: var(--cRed);
|
||||||
color: var(--cRed, $fallback--cRed);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ export default {
|
||||||
selector: '.panel',
|
selector: '.panel',
|
||||||
validInnerComponents: [
|
validInnerComponents: [
|
||||||
'Text',
|
'Text',
|
||||||
|
'Link',
|
||||||
'Icon',
|
'Icon',
|
||||||
'Button',
|
'Button',
|
||||||
'PanelHeader'
|
'PanelHeader'
|
||||||
|
|
|
@ -3,6 +3,7 @@ export default {
|
||||||
selector: '.panel-heading',
|
selector: '.panel-heading',
|
||||||
validInnerComponents: [
|
validInnerComponents: [
|
||||||
'Text',
|
'Text',
|
||||||
|
'Link',
|
||||||
'Icon',
|
'Icon',
|
||||||
'Button'
|
'Button'
|
||||||
]
|
]
|
||||||
|
|
|
@ -180,7 +180,7 @@
|
||||||
|
|
||||||
<span class="heading-right">
|
<span class="heading-right">
|
||||||
<router-link
|
<router-link
|
||||||
class="timeago faint-link"
|
class="timeago faint"
|
||||||
:to="{ name: 'conversation', params: { id: status.id } }"
|
:to="{ name: 'conversation', params: { id: status.id } }"
|
||||||
>
|
>
|
||||||
<Timeago
|
<Timeago
|
||||||
|
|
|
@ -113,12 +113,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.greentext {
|
.greentext {
|
||||||
color: $fallback--cGreen;
|
color: var(--textGreentext);
|
||||||
color: var(--postGreentext, $fallback--cGreen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cyantext {
|
.cyantext {
|
||||||
color: var(--postCyantext, $fallback--cBlue);
|
color: var(--textCyantext);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.-compact {
|
&.-compact {
|
||||||
|
|
|
@ -1,7 +1,43 @@
|
||||||
export default {
|
export default {
|
||||||
name: 'Text',
|
name: 'Text',
|
||||||
selector: '/*text*/',
|
selector: '/*text*/',
|
||||||
|
virtual: true,
|
||||||
|
variants: {
|
||||||
|
greentext: '.greentext'
|
||||||
|
},
|
||||||
states: {
|
states: {
|
||||||
faint: '.faint'
|
faint: '.faint'
|
||||||
}
|
},
|
||||||
|
defaultRules: [
|
||||||
|
{
|
||||||
|
component: 'Text',
|
||||||
|
directives: {
|
||||||
|
textColor: '--text'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Text',
|
||||||
|
state: ['faint'],
|
||||||
|
directives: {
|
||||||
|
textColor: '--text',
|
||||||
|
textOpacity: 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Text',
|
||||||
|
variant: 'greentext',
|
||||||
|
directives: {
|
||||||
|
textColor: '--cGreen'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Text',
|
||||||
|
variant: 'greentext',
|
||||||
|
state: ['faint'],
|
||||||
|
directives: {
|
||||||
|
textColor: '--cGreen',
|
||||||
|
textOpacity: 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export default {
|
export default {
|
||||||
name: 'Underlay',
|
name: 'Underlay',
|
||||||
selector: '#app',
|
selector: '#content',
|
||||||
|
outOfTreeSelector: '.underlay',
|
||||||
validInnerComponents: [
|
validInnerComponents: [
|
||||||
'Panel'
|
'Panel'
|
||||||
]
|
]
|
||||||
|
|
|
@ -143,12 +143,6 @@
|
||||||
box-shadow: var(--panelHeaderShadow);
|
box-shadow: var(--panelHeaderShadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
a,
|
|
||||||
.-link {
|
|
||||||
color: $fallback--link;
|
|
||||||
color: var(--panelLink, $fallback--link);
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-unstyled:hover,
|
.button-unstyled:hover,
|
||||||
a:hover {
|
a:hover {
|
||||||
i[class*="icon-"],
|
i[class*="icon-"],
|
||||||
|
@ -164,11 +158,6 @@
|
||||||
color: var(--panelFaint, $fallback--faint);
|
color: var(--panelFaint, $fallback--faint);
|
||||||
}
|
}
|
||||||
|
|
||||||
.faint-link {
|
|
||||||
color: $fallback--faint;
|
|
||||||
color: var(--faintLink, $fallback--faint);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.-flexible-height) {
|
&:not(.-flexible-height) {
|
||||||
> .button-default {
|
> .button-default {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
|
@ -173,7 +173,7 @@ export const mixrgb = (a, b) => {
|
||||||
* @returns {String} CSS rgba() color
|
* @returns {String} CSS rgba() color
|
||||||
*/
|
*/
|
||||||
export const rgba2css = function (rgba) {
|
export const rgba2css = function (rgba) {
|
||||||
return `rgba(${Math.floor(rgba.r)}, ${Math.floor(rgba.g)}, ${Math.floor(rgba.b)}, ${rgba.a})`
|
return `rgba(${Math.floor(rgba.r)}, ${Math.floor(rgba.g)}, ${Math.floor(rgba.b)}, ${rgba.a ?? 1})`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -188,7 +188,6 @@ export const rgba2css = function (rgba) {
|
||||||
*/
|
*/
|
||||||
export const getTextColor = function (bg, text, preserve) {
|
export const getTextColor = function (bg, text, preserve) {
|
||||||
const contrast = getContrastRatio(bg, text)
|
const contrast = getContrastRatio(bg, text)
|
||||||
console.log(contrast)
|
|
||||||
|
|
||||||
if (contrast < 4.5) {
|
if (contrast < 4.5) {
|
||||||
const base = typeof text.a !== 'undefined' ? { a: text.a } : {}
|
const base = typeof text.a !== 'undefined' ? { a: text.a } : {}
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
import { convert } from 'chromatism'
|
import { convert } from 'chromatism'
|
||||||
import { rgb2hex, hex2rgb, rgba2css, getCssColor, relativeLuminance } from '../color_convert/color_convert.js'
|
import { rgb2hex, hex2rgb, rgba2css, getCssColor, relativeLuminance } from '../color_convert/color_convert.js'
|
||||||
import { getColors, computeDynamicColor, getOpacitySlot } from '../theme_data/theme_data.service.js'
|
import { getColors, computeDynamicColor, getOpacitySlot } from '../theme_data/theme_data.service.js'
|
||||||
|
import { init } from '../theme_data/theme_data_3.service.js'
|
||||||
|
import {
|
||||||
|
sampleRules
|
||||||
|
} from 'src/services/theme_data/pleromafe.t3.js'
|
||||||
import { defaultState } from '../../modules/config.js'
|
import { defaultState } from '../../modules/config.js'
|
||||||
|
|
||||||
export const applyTheme = (input) => {
|
export const applyTheme = (input) => {
|
||||||
const { rules } = generatePreset(input)
|
const { rules, t3b } = generatePreset(input)
|
||||||
|
const themes3 = init(sampleRules, t3b)
|
||||||
const head = document.head
|
const head = document.head
|
||||||
const body = document.body
|
const body = document.body
|
||||||
body.classList.add('hidden')
|
body.classList.add('hidden')
|
||||||
|
@ -18,6 +23,10 @@ export const applyTheme = (input) => {
|
||||||
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 => {
|
||||||
|
console.log(rule)
|
||||||
|
styleSheet.insertRule(rule, 'index-max')
|
||||||
|
})
|
||||||
body.classList.remove('hidden')
|
body.classList.remove('hidden')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,7 +335,7 @@ export const generateShadows = (input, colors) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const composePreset = (colors, radii, shadows, fonts) => {
|
export const composePreset = (colors, radii, shadows, fonts, t3b) => {
|
||||||
return {
|
return {
|
||||||
rules: {
|
rules: {
|
||||||
...shadows.rules,
|
...shadows.rules,
|
||||||
|
@ -339,7 +348,8 @@ export const composePreset = (colors, radii, shadows, fonts) => {
|
||||||
...colors.theme,
|
...colors.theme,
|
||||||
...radii.theme,
|
...radii.theme,
|
||||||
...fonts.theme
|
...fonts.theme
|
||||||
}
|
},
|
||||||
|
t3b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,7 +359,8 @@ export const generatePreset = (input) => {
|
||||||
colors,
|
colors,
|
||||||
generateRadii(input),
|
generateRadii(input),
|
||||||
generateShadows(input, colors.theme.colors, colors.mod),
|
generateShadows(input, colors.theme.colors, colors.mod),
|
||||||
generateFonts(input)
|
generateFonts(input),
|
||||||
|
colors.theme.colors
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,30 +11,30 @@ export const sampleRules = [
|
||||||
{
|
{
|
||||||
component: 'Panel',
|
component: 'Panel',
|
||||||
directives: {
|
directives: {
|
||||||
background: '#FFFFFF',
|
background: '--fg'
|
||||||
opacity: 0.9
|
// opacity: 0.9
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'PanelHeader',
|
component: 'PanelHeader',
|
||||||
directives: {
|
directives: {
|
||||||
background: '#000000',
|
background: '--fg'
|
||||||
opacity: 0.9
|
// opacity: 0.9
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Button',
|
component: 'Button',
|
||||||
directives: {
|
directives: {
|
||||||
background: '#000000',
|
background: '--fg'
|
||||||
opacity: 0.8
|
// opacity: 0.8
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Button',
|
component: 'Button',
|
||||||
state: ['hover'],
|
state: ['hover'],
|
||||||
directives: {
|
directives: {
|
||||||
background: '#FF00FF',
|
background: '#FFFFFF'
|
||||||
opacity: 0.9
|
// opacity: 0.9
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { convert } from 'chromatism'
|
import { convert } from 'chromatism'
|
||||||
import { alphaBlend, getTextColor, rgba2css } from '../color_convert/color_convert.js'
|
import { alphaBlend, getTextColor, rgba2css, mixrgb } from '../color_convert/color_convert.js'
|
||||||
|
|
||||||
import Underlay from 'src/components/underlay.style.js'
|
import Underlay from 'src/components/underlay.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 Text from 'src/components/text.style.js'
|
import Text from 'src/components/text.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
|
const root = Underlay
|
||||||
|
@ -15,6 +16,7 @@ const components = {
|
||||||
PanelHeader,
|
PanelHeader,
|
||||||
Button,
|
Button,
|
||||||
Text,
|
Text,
|
||||||
|
Link,
|
||||||
Icon
|
Icon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,9 +37,9 @@ export const getAllPossibleCombinations = (array) => {
|
||||||
return combos.reduce((acc, x) => [...acc, ...x], [])
|
return combos.reduce((acc, x) => [...acc, ...x], [])
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ruleToSelector = (rule) => {
|
export const ruleToSelector = (rule, isParent) => {
|
||||||
const component = components[rule.component]
|
const component = components[rule.component]
|
||||||
const { states, variants, selector } = component
|
const { states, variants, selector, outOfTreeSelector } = component
|
||||||
|
|
||||||
const applicableStates = ((rule.state || []).filter(x => x !== 'normal')).map(state => states[state])
|
const applicableStates = ((rule.state || []).filter(x => x !== 'normal')).map(state => states[state])
|
||||||
|
|
||||||
|
@ -47,56 +49,104 @@ export const ruleToSelector = (rule) => {
|
||||||
applicableVariant = variants[applicableVariantName]
|
applicableVariant = variants[applicableVariantName]
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectors = [selector, applicableVariant, ...applicableStates]
|
let realSelector
|
||||||
|
if (isParent) {
|
||||||
|
realSelector = selector
|
||||||
|
} else {
|
||||||
|
if (outOfTreeSelector) realSelector = outOfTreeSelector
|
||||||
|
else realSelector = selector
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectors = [realSelector, applicableVariant, ...applicableStates]
|
||||||
.toSorted((a, b) => {
|
.toSorted((a, b) => {
|
||||||
if (a.startsWith(':')) return 1
|
if (a.startsWith(':')) return 1
|
||||||
else return -1
|
if (!a.startsWith('.')) return -1
|
||||||
|
else return 0
|
||||||
})
|
})
|
||||||
.join('')
|
.join('')
|
||||||
|
|
||||||
if (rule.parent) {
|
if (rule.parent) {
|
||||||
return ruleToSelector(rule.parent) + ' ' + selectors
|
return ruleToSelector(rule.parent, true) + ' ' + selectors
|
||||||
}
|
}
|
||||||
return selectors
|
return selectors
|
||||||
}
|
}
|
||||||
|
|
||||||
export const init = (ruleset) => {
|
export const init = (extraRuleset, palette) => {
|
||||||
const rootName = root.name
|
const rootName = root.name
|
||||||
const rules = []
|
const rules = []
|
||||||
const rulesByComponent = {}
|
const rulesByComponent = {}
|
||||||
|
|
||||||
|
const ruleset = [
|
||||||
|
...Object.values(components).map(c => c.defaultRules || []).reduce((acc, arr) => [...acc, ...arr], []),
|
||||||
|
...extraRuleset
|
||||||
|
]
|
||||||
|
|
||||||
const addRule = (rule) => {
|
const addRule = (rule) => {
|
||||||
rules.push(rule)
|
rules.push(rule)
|
||||||
rulesByComponent[rule.component] = rulesByComponent[rule.component] || []
|
rulesByComponent[rule.component] = rulesByComponent[rule.component] || []
|
||||||
rulesByComponent[rule.component].push(rule)
|
rulesByComponent[rule.component].push(rule)
|
||||||
}
|
}
|
||||||
|
|
||||||
const findRules = (combination) => rule => {
|
const findRules = (combination, parent) => rule => {
|
||||||
if (combination.component !== rule.component) return false
|
// inexact search
|
||||||
if (Object.prototype.hasOwnProperty.call(rule, 'variant')) {
|
const doesCombinationMatch = () => {
|
||||||
if (combination.variant !== rule.variant) return false
|
if (combination.component !== rule.component) return false
|
||||||
} else {
|
if (Object.prototype.hasOwnProperty.call(rule, 'variant')) {
|
||||||
if (combination.variant !== 'normal') return false
|
if (combination.variant !== rule.variant) return false
|
||||||
}
|
} else {
|
||||||
|
if (combination.variant !== 'normal') return false
|
||||||
|
}
|
||||||
|
|
||||||
if (Object.prototype.hasOwnProperty.call(rule, 'state')) {
|
if (Object.prototype.hasOwnProperty.call(rule, 'state')) {
|
||||||
const ruleStatesSet = new Set(['normal', ...(rule.state || [])])
|
const ruleStatesSet = new Set(['normal', ...(rule.state || [])])
|
||||||
const combinationSet = new Set(['normal', ...combination.state])
|
const combinationSet = new Set(['normal', ...combination.state])
|
||||||
const setsAreEqual = combination.state.every(state => ruleStatesSet.has(state)) &&
|
const setsAreEqual = combination.state.every(state => ruleStatesSet.has(state)) &&
|
||||||
[...ruleStatesSet].every(state => combinationSet.has(state))
|
[...ruleStatesSet].every(state => combinationSet.has(state))
|
||||||
return setsAreEqual
|
return setsAreEqual
|
||||||
} else {
|
} else {
|
||||||
if (combination.state.length !== 1 || combination.state[0] !== 'normal') return false
|
if (combination.state.length !== 1 || combination.state[0] !== 'normal') return false
|
||||||
return true
|
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) => {
|
const findLowerLevelRule = (parent, filter = () => true) => {
|
||||||
let lowerLevelComponent = null
|
let lowerLevelComponent = null
|
||||||
let currentParent = parent
|
let currentParent = parent
|
||||||
while (currentParent) {
|
while (currentParent) {
|
||||||
const rulesParent = ruleset.filter(findRules(currentParent, true))
|
const rulesParent = ruleset.filter(findRules(currentParent))
|
||||||
rulesParent > 1 && console.log('OOPS')
|
rulesParent > 1 && console.warn('OOPS')
|
||||||
lowerLevelComponent = rulesParent[rulesParent.length - 1]
|
lowerLevelComponent = rulesParent[rulesParent.length - 1]
|
||||||
currentParent = currentParent.parent
|
currentParent = currentParent.parent
|
||||||
if (lowerLevelComponent && filter(lowerLevelComponent)) currentParent = null
|
if (lowerLevelComponent && filter(lowerLevelComponent)) currentParent = null
|
||||||
|
@ -104,6 +154,35 @@ export const init = (ruleset) => {
|
||||||
return filter(lowerLevelComponent) ? lowerLevelComponent : null
|
return filter(lowerLevelComponent) ? lowerLevelComponent : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const findColor = (color) => {
|
||||||
|
if (typeof color === 'string' && color.startsWith('--')) {
|
||||||
|
const name = color.substring(2)
|
||||||
|
return palette[name]
|
||||||
|
}
|
||||||
|
return color
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTextColorAlpha = (rule, lowerRule, value) => {
|
||||||
|
const opacity = rule.directives.textOpacity
|
||||||
|
const textColor = convert(findColor(value)).rgb
|
||||||
|
if (opacity === null || opacity === undefined || opacity >= 1) {
|
||||||
|
return convert(textColor).hex
|
||||||
|
}
|
||||||
|
const backgroundColor = convert(lowerRule.cache.background).rgb
|
||||||
|
if (opacity === 0) {
|
||||||
|
return convert(backgroundColor).hex
|
||||||
|
}
|
||||||
|
const opacityMode = rule.directives.textOpacityMode
|
||||||
|
switch (opacityMode) {
|
||||||
|
case 'fake':
|
||||||
|
return convert(alphaBlend(textColor, opacity, backgroundColor)).hex
|
||||||
|
case 'mixrgb':
|
||||||
|
return convert(mixrgb(backgroundColor, textColor)).hex
|
||||||
|
default:
|
||||||
|
return rgba2css({ a: opacity, ...textColor })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const processInnerComponent = (component, parent) => {
|
const processInnerComponent = (component, parent) => {
|
||||||
const {
|
const {
|
||||||
validInnerComponents = [],
|
validInnerComponents = [],
|
||||||
|
@ -124,79 +203,156 @@ export const init = (ruleset) => {
|
||||||
const VIRTUAL_COMPONENTS = new Set(['Text', 'Link', 'Icon'])
|
const VIRTUAL_COMPONENTS = new Set(['Text', 'Link', 'Icon'])
|
||||||
|
|
||||||
stateVariantCombination.forEach(combination => {
|
stateVariantCombination.forEach(combination => {
|
||||||
const existingRules = ruleset.filter(findRules({ component: component.name, ...combination }))
|
let needRuleAdd = false
|
||||||
const lastRule = existingRules[existingRules.length - 1]
|
|
||||||
|
|
||||||
if (existingRules.length !== 0) {
|
if (VIRTUAL_COMPONENTS.has(component.name)) {
|
||||||
const { directives } = lastRule
|
const selector = component.name + ruleToSelector({ component: component.name, ...combination })
|
||||||
const rgb = convert(directives.background).rgb
|
const virtualName = [
|
||||||
|
'--',
|
||||||
|
component.name.toLowerCase(),
|
||||||
|
combination.variant === 'normal'
|
||||||
|
? ''
|
||||||
|
: combination.variant[0].toUpperCase() + combination.variant.slice(1).toLowerCase(),
|
||||||
|
...combination.state.filter(x => x !== 'normal').toSorted().map(state => state[0].toUpperCase() + state.slice(1).toLowerCase())
|
||||||
|
].join('')
|
||||||
|
|
||||||
// TODO: DEFAULT TEXT COLOR
|
const lowerLevel = findLowerLevelRule(parent, (r) => {
|
||||||
const bg = findLowerLevelRule(parent)?.cache.background || convert('#FFFFFF').rgb
|
if (components[r.component].validInnerComponents.indexOf(component.name) < 0) return false
|
||||||
|
if (r.cache.background === undefined) return false
|
||||||
|
if (r.cache.textDefined) {
|
||||||
|
return !r.cache.textDefined[selector]
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
if (!lastRule.cache?.background) {
|
if (!lowerLevel) return
|
||||||
const blend = directives.opacity < 1 ? alphaBlend(rgb, directives.opacity, bg) : rgb
|
|
||||||
lastRule.cache = lastRule.cache || {}
|
|
||||||
lastRule.cache.background = blend
|
|
||||||
|
|
||||||
addRule(lastRule)
|
let inheritedTextColorRule
|
||||||
|
const inheritedTextColorRules = findLowerLevelRule(parent, (r) => {
|
||||||
|
return r.cache?.textDefined?.[selector]
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!inheritedTextColorRule) {
|
||||||
|
const generalTextColorRules = ruleset.filter(findRules({ component: component.name, ...combination }, null, true))
|
||||||
|
inheritedTextColorRule = generalTextColorRules[generalTextColorRules.length - 1]
|
||||||
|
} else {
|
||||||
|
inheritedTextColorRule = inheritedTextColorRules[inheritedTextColorRules.length - 1]
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (VIRTUAL_COMPONENTS.has(component.name)) {
|
|
||||||
const selector = component.name + ruleToSelector({ component: component.name, ...combination })
|
|
||||||
|
|
||||||
const lowerLevel = findLowerLevelRule(parent, (r) => {
|
let inheritedTextColor
|
||||||
if (components[r.component].validInnerComponents.indexOf(component.name) < 0) return false
|
let inheritedTextOpacity = {}
|
||||||
if (r.cache?.background === undefined) return false
|
if (inheritedTextColorRule) {
|
||||||
if (r.cache.textDefined) {
|
inheritedTextColor = findColor(inheritedTextColorRule.directives.textColor)
|
||||||
return !r.cache.textDefined[selector]
|
// also inherit opacity settings
|
||||||
|
const { textOpacity, textOpacityMode } = inheritedTextColorRule.directives
|
||||||
|
inheritedTextOpacity = { textOpacity, textOpacityMode }
|
||||||
|
} else {
|
||||||
|
// Emergency fallback
|
||||||
|
inheritedTextColor = '#000000'
|
||||||
|
}
|
||||||
|
|
||||||
|
const textColor = getTextColor(
|
||||||
|
convert(lowerLevel.cache.background).rgb,
|
||||||
|
convert(inheritedTextColor).rgb,
|
||||||
|
component.name === 'Link' // make it configurable?
|
||||||
|
)
|
||||||
|
|
||||||
|
lowerLevel.cache.textDefined = lowerLevel.cache.textDefined || {}
|
||||||
|
lowerLevel.cache.textDefined[selector] = textColor
|
||||||
|
lowerLevel.virtualDirectives = lowerLevel.virtualDirectives || {}
|
||||||
|
lowerLevel.virtualDirectives[virtualName] = getTextColorAlpha(inheritedTextColorRule, lowerLevel, textColor)
|
||||||
|
|
||||||
|
const directives = {
|
||||||
|
textColor,
|
||||||
|
...inheritedTextOpacity
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug: lets you see what it think background color should be
|
||||||
|
directives.background = convert(lowerLevel.cache.background).hex
|
||||||
|
|
||||||
|
addRule({
|
||||||
|
parent,
|
||||||
|
virtual: true,
|
||||||
|
component: component.name,
|
||||||
|
...combination,
|
||||||
|
cache: { background: lowerLevel.cache.background },
|
||||||
|
directives
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const existingGlobalRules = ruleset.filter(findRules({ component: component.name, ...combination }, null))
|
||||||
|
const existingRules = ruleset.filter(findRules({ component: component.name, ...combination }, parent))
|
||||||
|
|
||||||
|
// Global (general) rules
|
||||||
|
if (existingGlobalRules.length !== 0) {
|
||||||
|
const lastRule = existingGlobalRules[existingGlobalRules.length - 1]
|
||||||
|
const { directives } = lastRule
|
||||||
|
lastRule.cache = lastRule.cache || {}
|
||||||
|
|
||||||
|
if (directives.background) {
|
||||||
|
const rgb = convert(findColor(directives.background)).rgb
|
||||||
|
|
||||||
|
// TODO: DEFAULT TEXT COLOR
|
||||||
|
const bg = findLowerLevelRule(parent)?.cache.background || convert('#FFFFFF').rgb
|
||||||
|
|
||||||
|
if (!lastRule.cache.background) {
|
||||||
|
const blend = directives.opacity < 1 ? alphaBlend(rgb, directives.opacity, bg) : rgb
|
||||||
|
lastRule.cache.background = blend
|
||||||
|
|
||||||
|
needRuleAdd = true
|
||||||
}
|
}
|
||||||
return true
|
}
|
||||||
})
|
|
||||||
if (!lowerLevel) return
|
if (needRuleAdd) {
|
||||||
lowerLevel.cache.textDefined = lowerLevel.cache.textDefined || {}
|
addRule(lastRule)
|
||||||
lowerLevel.cache.textDefined[selector] = true
|
}
|
||||||
addRule({
|
}
|
||||||
parent,
|
|
||||||
component: component.name,
|
if (existingRules.length !== 0) {
|
||||||
...combination,
|
console.warn('MORE EXISTING RULES', existingRules)
|
||||||
directives: {
|
|
||||||
// TODO: DEFAULT TEXT COLOR
|
|
||||||
textColor: getTextColor(convert(lowerLevel.cache.background).rgb, convert('#FFFFFF').rgb, component.name === 'Link'),
|
|
||||||
// Debug: lets you see what it think background color should be
|
|
||||||
background: convert(lowerLevel.cache.background).hex
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
innerComponents.forEach(innerComponent => processInnerComponent(innerComponent, { parent, component: name, ...combination }))
|
innerComponents.forEach(innerComponent => processInnerComponent(innerComponent, { parent, component: name, ...combination }))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
processInnerComponent(components[rootName])
|
processInnerComponent(components[rootName])
|
||||||
|
|
||||||
// console.info(rules.map(x => [
|
|
||||||
// (parent?.component || 'root') + ' -> ' + x.component,
|
|
||||||
// // 'Cached background:' + convert(bg).hex,
|
|
||||||
// // 'Color: ' + convert(x.directives.background).hex + ' A:' + x.directives.opacity,
|
|
||||||
// JSON.stringify(x.directives)
|
|
||||||
// // '=> Blend: ' + convert(x.cache.background).hex
|
|
||||||
// ].join(' ')))
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
raw: rules,
|
raw: rules,
|
||||||
css: rules.map(rule => {
|
css: rules.map(rule => {
|
||||||
const header = ruleToSelector(rule) + ' {'
|
if (rule.virtual) return ''
|
||||||
|
|
||||||
|
let selector = ruleToSelector(rule).replace(/\/\*.*\*\//g, '')
|
||||||
|
if (!selector) {
|
||||||
|
selector = 'body'
|
||||||
|
}
|
||||||
|
const header = selector + ' {'
|
||||||
const footer = '}'
|
const footer = '}'
|
||||||
|
|
||||||
|
const virtualDirectives = Object.entries(rule.virtualDirectives || {}).map(([k, v]) => {
|
||||||
|
return ' ' + k + ': ' + v
|
||||||
|
}).join(';\n')
|
||||||
|
|
||||||
const directives = Object.entries(rule.directives).map(([k, v]) => {
|
const directives = Object.entries(rule.directives).map(([k, v]) => {
|
||||||
switch (k) {
|
switch (k) {
|
||||||
case 'background': return 'background-color: ' + rgba2css({ ...convert(v).rgb, a: rule.directives.opacity ?? 1 })
|
case 'background': {
|
||||||
case 'textColor': return 'color: ' + rgba2css({ ...convert(v).rgb, a: rule.directives.opacity ?? 1 })
|
return 'background-color: ' + rgba2css({ ...convert(findColor(v)).rgb, a: rule.directives.opacity ?? 1 })
|
||||||
|
}
|
||||||
|
case 'textColor': {
|
||||||
|
return 'color: ' + v
|
||||||
|
}
|
||||||
default: return ''
|
default: return ''
|
||||||
}
|
}
|
||||||
}).filter(x => x).map(x => ' ' + x).join(';\n')
|
}).filter(x => x).map(x => ' ' + x).join(';\n')
|
||||||
return [header, directives, footer].join('\n')
|
|
||||||
})
|
return [
|
||||||
|
header,
|
||||||
|
directives + ';',
|
||||||
|
' color: var(--text);',
|
||||||
|
'',
|
||||||
|
virtualDirectives,
|
||||||
|
footer
|
||||||
|
].join('\n')
|
||||||
|
}).filter(x => x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ describe.only('Theme Data 3', () => {
|
||||||
|
|
||||||
describe('init', () => {
|
describe('init', () => {
|
||||||
it('test simple case', () => {
|
it('test simple case', () => {
|
||||||
const out = init(sampleRules)
|
const out = init(sampleRules, palette)
|
||||||
// console.log(JSON.stringify(out, null, 2))
|
// console.log(JSON.stringify(out, null, 2))
|
||||||
console.log('\n' + out.css.join('\n') + '\n')
|
console.log('\n' + out.css.join('\n') + '\n')
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue