commit: 98f972e557047a626880692189fdae68269a732e
parent 9ec61d0f0a26369196d9e9b4ac62f4a5b3c38ce1
Author: Henry Jameson <me@hjkos.com>
Date: Sun, 11 Feb 2024 23:11:28 +0200
menu-item improvements
Diffstat:
16 files changed, 209 insertions(+), 251 deletions(-)
diff --git a/src/App.scss b/src/App.scss
@@ -129,15 +129,6 @@ i[class*="icon-"],
color: var(--icon);
}
-.button-unstyled:hover,
-a:hover {
- > i[class*="icon-"],
- > .svg-inline--fa,
- > .iconLetter {
- color: var(--text);
- }
-}
-
nav {
z-index: var(--ZI_navbar);
background-color: $fallback--fg;
@@ -192,8 +183,7 @@ nav {
grid-column: 1 / span 3;
grid-row: 1 / 1;
pointer-events: none;
- background-color: rgb(0 0 0 / 15%);
- background-color: var(--underlay, rgb(0 0 0 / 15%));
+ background-color: var(--underlay);
z-index: -1000;
}
@@ -363,10 +353,7 @@ nav {
.button-default {
user-select: none;
- color: $fallback--text;
- color: var(--btnText, $fallback--text);
- background-color: $fallback--fg;
- background-color: var(--btn, $fallback--fg);
+ color: var(--text);
border: none;
border-radius: $fallback--btnRadius;
border-radius: var(--btnRadius, $fallback--btnRadius);
@@ -376,70 +363,12 @@ nav {
font-family: sans-serif;
font-family: var(--interfaceFont, sans-serif);
- &.-sublime {
- background: transparent;
- }
-
- i[class*="icon-"],
- .svg-inline--fa {
- color: var(--icon);
- }
-
&::-moz-focus-inner {
border: none;
}
- &:active {
- color: $fallback--text;
- color: var(--btnPressedText, $fallback--text);
- background-color: $fallback--fg;
- background-color: var(--btnPressed, $fallback--fg);
-
- svg,
- i {
- color: $fallback--text;
- color: var(--btnPressedText, $fallback--text);
- }
- }
-
&:disabled {
cursor: not-allowed;
- color: $fallback--text;
- color: var(--btnDisabledText, $fallback--text);
- background-color: $fallback--fg;
- background-color: var(--btnDisabled, $fallback--fg);
-
- svg,
- i {
- color: $fallback--text;
- color: var(--btnDisabledText, $fallback--text);
- }
- }
-
- &.toggled {
- color: $fallback--text;
- color: var(--btnToggledText, $fallback--text);
- background-color: $fallback--fg;
- background-color: var(--btnToggled, $fallback--fg);
- 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);
-
- svg,
- i {
- color: $fallback--text;
- color: var(--btnToggledText, $fallback--text);
- }
- }
-
- &.danger {
- // TODO: add better color variable
- color: $fallback--text;
- color: var(--alertErrorPanelText, $fallback--text);
- background-color: $fallback--alertError;
- background-color: var(--alertError, $fallback--alertError);
}
}
@@ -458,8 +387,7 @@ nav {
color: inherit;
&.-link {
- color: $fallback--link;
- color: var(--link, $fallback--link);
+ color: var(--link);
}
&.-fullwidth {
@@ -510,7 +438,6 @@ textarea {
&[disabled="disabled"],
&.disabled {
cursor: not-allowed;
- opacity: 0.5;
}
&[type="range"] {
@@ -610,9 +537,7 @@ textarea {
}
option {
- color: $fallback--text;
- color: var(--text, $fallback--text);
- background-color: $fallback--bg;
+ color: var(--text);
background-color: var(--bg, $fallback--bg);
}
@@ -746,6 +671,7 @@ option {
.faint {
--text: var(--textFaint);
--textGreentext: var(--textGreentextFaint);
+ --textCyantext: var(--textCyantextFaint);
--link: var(--linkFaint);
color: var(--text);
diff --git a/src/components/border.style.js b/src/components/border.style.js
@@ -0,0 +1,13 @@
+export default {
+ name: 'Border',
+ selector: '/*border*/',
+ virtual: true,
+ defaultRules: [
+ {
+ directives: {
+ textColor: '$mod(--parent, 10)',
+ textAuto: 'no-auto'
+ }
+ }
+ ]
+}
diff --git a/src/components/button.style.js b/src/components/button.style.js
@@ -23,28 +23,37 @@ const hoverGlow = {
export default {
name: 'Button', // Name of the component
selector: '.button', // CSS selector/prefix
- // States, system witll calculate ALL possible combinations of those and append a "normal" to them + standalone "normal" state
+ // outOfTreeSelector: '' // out-of-tree selector is used when other components are laid over it but it's not part of the tree, see Underlay component
+ // States, system witll calculate ALL possible combinations of those and prepend "normal" to them + standalone "normal" state
states: {
- // normal: state is implicitly added
+ // States are a bit expensive - the amount of combinations generated is about (1/6)n^3+n, so adding more state increased number of combination by an order of magnitude!
+ // All states inherit from "normal" state, there is no other inheirtance, i.e. hover+disabled only inherits from "normal", not from hover nor disabled.
+ // However, cascading still works, so resulting state will be result of merging of all relevant states/variants
+ // normal: '' // normal state is implicitly added, it is always included
disabled: ':disabled',
toggled: '.toggled',
pressed: ':active',
hover: ':hover',
focused: ':focus-within'
},
- // Variants are mutually exclusive, which saves on computation time
+ // Variants are mutually exclusive, each component implicitly has "normal" variant, and all other variants inherit from it.
variants: {
- normal: '-default', // you can override normal variant
- danger: '.danger',
- unstyled: '-unstyled'
+ // Variants save on computation time since adding new variant just adds one more "set".
+ normal: '-default', // you can override normal variant, it will be appenended to the main class
+ danger: '-default.danger'
+ // Overall the compuation difficulty is N*((1/6)M^3+M) where M is number of distinct states and N is number of variants.
+ // This (currently) is further multipled by number of places where component can exist.
},
+ // This lists all other components that can possibly exist within one. Recursion is currently not supported (and probably won't be supported ever).
validInnerComponents: [
'Text',
'Icon'
],
+ // Default rules, used as "default theme", essentially.
defaultRules: [
{
- component: 'Button',
+ // component: 'Button', // no need to specify components every time unless you're specifying how other component should look
+ // like within it
directives: {
background: '--fg',
shadow: [{
@@ -58,7 +67,6 @@ export default {
}
},
{
- component: 'Button',
variant: 'unstyled',
directives: {
background: '--fg',
@@ -66,14 +74,12 @@ export default {
}
},
{
- component: 'Button',
state: ['hover'],
directives: {
shadow: [hoverGlow, ...buttonInsetFakeBorders]
}
},
{
- component: 'Button',
state: ['hover', 'pressed'],
directives: {
background: '--accent,-24.2',
@@ -81,7 +87,6 @@ export default {
}
},
{
- component: 'Button',
state: ['disabled'],
directives: {
background: '$blend(--background, 0.25, --parent)',
diff --git a/src/components/dropdown_menu.style.js b/src/components/dropdown_menu.style.js
@@ -1,19 +0,0 @@
-export default {
- name: 'DropdownMenu',
- selector: '.dropdown',
- validInnerComponents: [
- 'Text',
- 'Icon',
- 'Input'
- ],
- states: {
- hover: ':hover'
- },
- defaultRules: [
- {
- directives: {
- background: '--fg'
- }
- }
- ]
-}
diff --git a/src/components/menu_item.style.js b/src/components/menu_item.style.js
@@ -0,0 +1,45 @@
+export default {
+ name: 'MenuItem',
+ selector: '.menu-item',
+ validInnerComponents: [
+ 'Text',
+ 'Icon',
+ 'Input',
+ 'Border'
+ ],
+ states: {
+ hover: ':hover',
+ active: 'active'
+ },
+ defaultRules: [
+ {
+ directives: {
+ background: '--fg'
+ }
+ },
+ {
+ component: 'Text',
+ variant: 'normal',
+ parent: {
+ component: 'MenuItem',
+ state: ['normal', 'hover'],
+ variant: 'normal'
+ },
+ directives: {
+ textColor: '--link',
+ textAuto: 'no-preserve'
+ }
+ },
+ {
+ component: 'Icon',
+ parent: {
+ component: 'MenuItem',
+ state: ['hover']
+ },
+ directives: {
+ textColor: '--link',
+ textAuto: 'no-preserve'
+ }
+ }
+ ]
+}
diff --git a/src/components/modals.style.js b/src/components/modals.style.js
@@ -0,0 +1,8 @@
+export default {
+ name: 'Modals',
+ selector: '.modal-view',
+ validInnerComponents: [
+ 'Panel'
+ ],
+ defaultRules: []
+}
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
@@ -107,7 +107,7 @@
.NavPanel {
.panel {
overflow: hidden;
- box-shadow: var(--panelShadow);
+ box-shadow: var(--shadow);
}
ul {
@@ -124,14 +124,14 @@
}
> li {
- &:first-child .menu-item {
+ &:first-child.menu-item {
border-top-right-radius: $fallback--panelRadius;
border-top-right-radius: var(--panelRadius, $fallback--panelRadius);
border-top-left-radius: $fallback--panelRadius;
border-top-left-radius: var(--panelRadius, $fallback--panelRadius);
}
- &:last-child .menu-item {
+ &:last-child.menu-item {
border-bottom-right-radius: $fallback--panelRadius;
border-bottom-right-radius: var(--panelRadius, $fallback--panelRadius);
border-bottom-left-radius: $fallback--panelRadius;
diff --git a/src/components/navigation/navigation_entry.vue b/src/components/navigation/navigation_entry.vue
@@ -1,7 +1,6 @@
<template>
<OptionalRouterLink
v-slot="{ isActive, href, navigate } = {}"
- ass="ass"
:to="routeTo"
>
<li
@@ -96,40 +95,5 @@
margin-right: -0.8em;
}
}
-
- &:hover {
- background-color: $fallback--lightBg;
- background-color: var(--selectedMenu, $fallback--lightBg);
- color: $fallback--link;
- color: var(--selectedMenuText, $fallback--link);
-
- --faint: var(--selectedMenuFaintText, $fallback--faint);
- --faintLink: var(--selectedMenuFaintLink, $fallback--faint);
- --lightText: var(--selectedMenuLightText, $fallback--lightText);
-
- .menu-icon {
- --icon: var(--text, $fallback--icon);
- }
- }
-
- &.-active {
- font-weight: bolder;
- background-color: $fallback--lightBg;
- background-color: var(--selectedMenu, $fallback--lightBg);
- color: $fallback--text;
- color: var(--selectedMenuText, $fallback--text);
-
- --faint: var(--selectedMenuFaintText, $fallback--faint);
- --faintLink: var(--selectedMenuFaintLink, $fallback--faint);
- --lightText: var(--selectedMenuLightText, $fallback--lightText);
-
- .menu-icon {
- --icon: var(--text, $fallback--icon);
- }
-
- &:hover {
- text-decoration: underline;
- }
- }
}
</style>
diff --git a/src/components/panel.style.js b/src/components/panel.style.js
@@ -7,12 +7,21 @@ export default {
'Icon',
'Button',
'Input',
- 'PanelHeader'
+ 'PanelHeader',
+ 'MenuItem'
],
defaultRules: [
{
directives: {
- background: '--bg'
+ background: '--bg',
+ shadow: [{
+ x: 1,
+ y: 1,
+ blur: 4,
+ spread: 0,
+ color: '#000000',
+ alpha: 0.6
+ }]
}
}
]
diff --git a/src/components/panel_header.style.js b/src/components/panel_header.style.js
@@ -11,7 +11,8 @@ export default {
{
component: 'PanelHeader',
directives: {
- background: '--fg'
+ background: '--fg',
+ shadow: []
}
}
]
diff --git a/src/components/popover.style.js b/src/components/popover.style.js
@@ -1,6 +1,10 @@
export default {
name: 'Popover',
selector: '.popover',
+ variants: {
+ tooltip: '.tooltip',
+ modal: '.modal'
+ },
validInnerComponents: [
'Text',
'Link',
@@ -8,12 +12,20 @@ export default {
'Button',
'Input',
'PanelHeader',
- 'DropdownMenu'
+ 'MenuItem'
],
defaultRules: [
{
directives: {
- background: '--fg'
+ background: '--bg',
+ shadow: [{
+ x: 2,
+ y: 2,
+ blur: 3,
+ spread: 0,
+ color: '#000000',
+ alpha: 0.5
+ }]
}
}
]
diff --git a/src/components/root.style.js b/src/components/root.style.js
@@ -3,8 +3,8 @@ export default {
selector: ':root',
validInnerComponents: [
'Underlay',
- 'TopBar',
- 'Popover'
+ 'Modals',
+ 'TopBar'
],
defaultRules: [
{
diff --git a/src/components/underlay.style.js b/src/components/underlay.style.js
@@ -1,6 +1,9 @@
export default {
name: 'Underlay',
- selector: 'body', // Should be '#content' but for now this is better for testing until I have proper popovers and such...
+ selector: '#content',
+ // Out of tree selector: Most components are laid over underlay, but underlay itself is not part of the DOM tree,
+ // i.e. it's a separate absolutely-positioned component, so we need to treat it differently depending on whether
+ // we are searching for underlay specifically or for whatever is laid on top of it.
outOfTreeSelector: '.underlay',
validInnerComponents: [
'Panel'
diff --git a/src/panel.scss b/src/panel.scss
@@ -3,8 +3,6 @@
position: relative;
display: flex;
flex-direction: column;
- background-color: $fallback--bg;
- background-color: var(--bg, $fallback--bg);
.panel-heading {
background-color: inherit;
@@ -24,8 +22,7 @@
left: 0;
right: 0;
z-index: 5;
- box-shadow: 1px 1px 4px rgb(0 0 0 / 60%);
- box-shadow: var(--panelShadow);
+ box-shadow: var(--shadow);
pointer-events: none;
}
}
@@ -133,58 +130,18 @@
border-radius: var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius) 0 0;
border-width: 0 0 1px;
align-items: start;
- // panel theme
- color: var(--panelText);
&::after {
background-color: var(--background);
z-index: -2;
border-radius: $fallback--panelRadius $fallback--panelRadius 0 0;
border-radius: var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius) 0 0;
- box-shadow: var(--panelHeaderShadow);
- }
-
- .button-unstyled:hover,
- a:hover {
- i[class*="icon-"],
- .svg-inline--fa,
- .iconLetter {
- color: var(--panelText);
- }
- }
-
- .faint {
- background-color: transparent;
- color: $fallback--faint;
- color: var(--panelFaint, $fallback--faint);
+ box-shadow: var(--shadow);
}
&:not(.-flexible-height) {
> .button-default {
flex-shrink: 0;
-
- &,
- i[class*="icon-"] {
- color: $fallback--text;
- color: var(--btnPanelText, $fallback--text);
- }
-
- &:active {
- background-color: $fallback--fg;
- background-color: var(--btnPressedPanel, $fallback--fg);
- color: $fallback--text;
- color: var(--btnPressedPanelText, $fallback--text);
- }
-
- &:disabled {
- color: $fallback--text;
- color: var(--btnDisabledPanelText, $fallback--text);
- }
-
- &.toggled {
- color: $fallback--text;
- color: var(--btnToggledPanelText, $fallback--text);
- }
}
}
@@ -227,6 +184,6 @@
align-items: center;
border-width: 1px 0 0;
border-style: solid;
- border-color: var(--border, $fallback--border);
+ border-color: var(--border);
}
/* stylelint-enable no-descending-specificity */
diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js
@@ -11,7 +11,8 @@ 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 Popover from 'src/components/popover.style.js'
-import DropdownMenu from 'src/components/dropdown_menu.style.js'
+import Modals from 'src/components/modals.style.js'
+import MenuItem from 'src/components/menu_item.style.js'
import Panel from 'src/components/panel.style.js'
import PanelHeader from 'src/components/panel_header.style.js'
import Button from 'src/components/button.style.js'
@@ -19,34 +20,11 @@ import Input from 'src/components/input.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 Border from 'src/components/border.style.js'
const DEBUG = false
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,
@@ -54,9 +32,7 @@ export const DEFAULT_SHADOWS = {
spread: 0,
color: '#000000',
alpha: 0.7
- }],
- avatarStatus: [],
- panelHeader: []
+ }]
}
const components = {
@@ -64,9 +40,11 @@ const components = {
Text,
Link,
Icon,
+ Border,
Underlay,
+ Modals,
Popover,
- DropdownMenu,
+ MenuItem,
Panel,
PanelHeader,
TopBar,
@@ -196,16 +174,41 @@ export const init = (extraRuleset, palette) => {
const rules = []
- const ruleset = [
- ...Object.values(components).map(c => c.defaultRules.map(r => ({ component: c.name, ...r })) || []).reduce((acc, arr) => [...acc, ...arr], []),
- ...extraRuleset
- ].map(rule => {
+ const normalizeCombination = rule => {
rule.variant = rule.variant ?? 'normal'
rule.state = [...new Set(['normal', ...(rule.state || [])])]
+ }
+
+ const rulesetUnsorted = [
+ ...Object.values(components)
+ .map(c => (c.defaultRules || []).map(r => ({ component: c.name, ...r })))
+ .reduce((acc, arr) => [...acc, ...arr], []),
+ ...extraRuleset
+ ].map(rule => {
+ normalizeCombination(rule)
+ let currentParent = rule.parent
+ while (currentParent) {
+ normalizeCombination(currentParent)
+ currentParent = currentParent.parent
+ }
return rule
})
+ const ruleset = rulesetUnsorted
+ .map((data, index) => ({ data, index }))
+ .sort(({ data: a, index: ai }, { data: b, index: bi }) => {
+ const parentsA = unroll(a).length
+ const parentsB = unroll(b).length
+
+ if (parentsA === parentsB || (parentsB !== 0 && parentsA !== 0)) return ai - bi
+ if (parentsA === 0 && parentsB !== 0) return -1
+ if (parentsB === 0 && parentsA !== 0) return 1
+ return 0 // failsafe, shouldn't happen?
+ })
+ .map(({ data }) => data)
+
+ console.log(ruleset)
const virtualComponents = new Set(Object.values(components).filter(c => c.virtual).map(c => c.name))
const addRule = (rule) => {
@@ -260,6 +263,18 @@ export const init = (extraRuleset, palette) => {
targetColor = alphaBlend(backgroundArg, amount, foregroundArg)
break
}
+ case 'mod': {
+ if (args.length !== 2) {
+ throw new Error(`$mod requires 2 arguments, ${args.length} were provided`)
+ }
+ const color = convert(findColor(args[0], dynamicVars)).rgb
+ const amount = Number(args[1])
+ const effectiveBackground = dynamicVars.lowerLevelBackground
+ const isLightOnDark = relativeLuminance(convert(effectiveBackground).rgb) < 0.5
+ const mod = isLightOnDark ? 1 : -1
+ targetColor = brightness(amount * mod, color).rgb
+ break
+ }
}
} catch (e) {
console.error('Failure executing color function', e)
@@ -332,12 +347,12 @@ export const init = (extraRuleset, palette) => {
}
const processInnerComponent = (component, parent) => {
- const parentList = parent ? unroll(parent).reverse().map(c => c.component) : []
- if (!component.virtual) {
- const path = [...parentList, component.name].join(' > ')
- console.log('Component ' + path + ' process starting')
- }
- const t0 = performance.now()
+ // const parentList = parent ? unroll(parent).reverse().map(c => c.component) : []
+ // if (!component.virtual) {
+ // const path = [...parentList, component.name].join(' > ')
+ // console.log('Component ' + path + ' process starting')
+ // }
+ // const t0 = performance.now()
const {
validInnerComponents = [],
states: originalStates = {},
@@ -359,7 +374,7 @@ export const init = (extraRuleset, palette) => {
}).reduce((acc, x) => [...acc, ...x], [])
stateVariantCombination.forEach(combination => {
- const tt0 = performance.now()
+ // const tt0 = performance.now()
const soloSelector = ruleToSelector({ component: component.name, ...combination }, true)
const selector = ruleToSelector({ component: component.name, ...combination, parent }, true)
@@ -422,6 +437,11 @@ export const init = (extraRuleset, palette) => {
}
}
+ if (component.name === 'Text' && combination.variant === 'normal' && computedRule.parent.component === 'MenuItem' && computedRule.parent.state.indexOf('hover') >= 0) {
+ console.log(existingRules)
+ console.log(computedRule, newTextRule)
+ }
+
dynamicVars.inheritedBackground = lowerLevelBackground
// TODO properly provide "parent" text color?
@@ -518,17 +538,17 @@ export const init = (extraRuleset, palette) => {
}
innerComponents.forEach(innerComponent => processInnerComponent(innerComponent, { parent, component: name, ...combination }))
- const tt1 = performance.now()
- if (!component.virtual) {
- console.log('State-variant ' + combination.variant + ' : ' + combination.state.join('+') + ' procession time: ' + (tt1 - tt0) + 'ms')
- }
+ // const tt1 = performance.now()
+ // if (!component.virtual) {
+ // console.log('State-variant ' + combination.variant + ' : ' + combination.state.join('+') + ' procession time: ' + (tt1 - tt0) + 'ms')
+ // }
})
- const t1 = performance.now()
- if (!component.virtual) {
- const path = [...parentList, component.name].join(' > ')
- console.log('Component ' + path + ' procession time: ' + (t1 - t0) + 'ms')
- }
+ // const t1 = performance.now()
+ // if (!component.virtual) {
+ // const path = [...parentList, component.name].join(' > ')
+ // console.log('Component ' + path + ' procession time: ' + (t1 - t0) + 'ms')
+ // }
}
processInnerComponent(components.Root)
diff --git a/top_bar.style.js b/top_bar.style.js
@@ -5,5 +5,19 @@ export default {
'Link',
'Text',
'Icon'
+ ],
+ defaultRules: [
+ {
+ directives: {
+ shadow: [{
+ x: 0,
+ y: 0,
+ blur: 4,
+ spread: 0,
+ color: '#000000',
+ alpha: 0.6
+ }]
+ }
+ }
]
}