Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use CSS variables for sprite images #1020

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
696 changes: 329 additions & 367 deletions packages/webamp/css/base-skin.css

Large diffs are not rendered by default.

30 changes: 27 additions & 3 deletions packages/webamp/css/webamp.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,48 @@
outline: 0;
}

#webamp [style*="--active-background"]:active {
background-image: var(--active-background);
}
#webamp [style*="--base-background"] {
background-image: var(--base-background);
}

/**
* Ideally we would collapse these next four down into two rules that each have
* multiple comma separated selectors. However, `::-moz-range-thumb`
* will break the rule in non-Firefox browsers, so we just make each
* selector it's own rule.
*/

#webamp [style*="--thumb-background"]::-webkit-slider-thumb {
background-image: var(--thumb-background);
}
#webamp [style*="--thumb-background"]::-moz-range-thumb {
background-image: var(--thumb-background);
}
#webamp [style*="--activeThumb-background"]:active::-webkit-slider-thumb {
background-image: var(--activeThumb-background);
}
#webamp [style*="--activeThumb-background"]:active::-moz-range-thumb {
background-image: var(--activeThumb-background);
}

/* Range input css reset */
#webamp input[type="range"] {
-webkit-appearance: none;
margin: 0;
padding: 0;
background: none;
border: none;
}
#webamp input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
border: none;
border-radius: 0;
background: none;
}
#webamp input[type="range"]::-moz-range-thumb {
border: none;
border-radius: 0;
background: none;
}
#webamp input[type="range"]::-moz-range-track {
border: none;
Expand Down
12 changes: 10 additions & 2 deletions packages/webamp/js/components/EqualizerWindow/EqualizerShade.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,16 @@ const EqualizerShade = () => {
>
<div id="equalizer-shade" onClick={toggleEqualizerShadeMode} />
<div id="equalizer-close" onClick={() => closeWindow("equalizer")} />
<Volume id="equalizer-volume" className={eqVolumeClassName} />
<Balance id="equalizer-balance" className={eqBalanceClassName} />
<Volume
id="equalizer-volume"
style={{ background: "none" }}
className={eqVolumeClassName}
/>
<Balance
style={{ background: "none" }}
id="equalizer-balance"
className={eqBalanceClassName}
/>
</div>
);
};
Expand Down
9 changes: 7 additions & 2 deletions packages/webamp/js/components/GenWindow/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as Selectors from "../../selectors";
import ResizeTarget from "../ResizeTarget";
import { WindowId } from "../../types";
import FocusTarget from "../FocusTarget";
import { useActionCreator, useTypedSelector } from "../../hooks";
import { useActionCreator, useSprite, useTypedSelector } from "../../hooks";

interface TextProps {
children: string;
Expand Down Expand Up @@ -54,13 +54,18 @@ export const GenWindow = ({ children, title, windowId, onKeyDown }: Props) => {
const windowSize = getWindowSize(windowId);
const selected = focusedWindow === windowId;
const { width, height } = getWindowPixelSize(windowId);
const topStyle = useSprite(
selected
? { base: "GEN_TOP_CENTER_FILL_SELECTED" }
: { base: "GEN_TOP_CENTER_FILL" }
);
return (
<FocusTarget windowId={windowId} onKeyDown={onKeyDown}>
<div
className={classnames("gen-window", "window", { selected })}
style={{ width, height }}
>
<div className="gen-top draggable">
<div className="gen-top draggable" style={topStyle}>
<div className="gen-top-left draggable" />
<div className="gen-top-left-fill draggable" />
<div className="gen-top-left-end draggable" />
Expand Down
15 changes: 13 additions & 2 deletions packages/webamp/js/components/MainWindow/Eject.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
import React from "react";

import * as Actions from "../../actionCreators";
import { useActionCreator } from "../../hooks";
import { useActionCreator, useSprite } from "../../hooks";

const Eject = React.memo(() => {
const openMediaFileDialog = useActionCreator(Actions.openMediaFileDialog);
return <div id="eject" onClick={openMediaFileDialog} title="Open File(s)" />;
const spriteStyle = useSprite({
base: "MAIN_EJECT_BUTTON",
size: "MAIN_EJECT_BUTTON",
active: "MAIN_EJECT_BUTTON_ACTIVE",
});
return (
<div
style={{ ...spriteStyle, position: "absolute", top: 89, left: 136 }}
onClick={openMediaFileDialog}
title="Open File(s)"
/>
);
});

export default Eject;
17 changes: 15 additions & 2 deletions packages/webamp/js/components/MainWindow/MainBalance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";

import Balance from "../Balance";
import * as Selectors from "../../selectors";
import { useTypedSelector } from "../../hooks";
import { useSprite, useTypedSelector } from "../../hooks";

export const offsetFromBalance = (balance: number): number => {
const percent = Math.abs(balance) / 100;
Expand All @@ -13,10 +13,23 @@ export const offsetFromBalance = (balance: number): number => {

const MainBalance = React.memo(() => {
const balance = useTypedSelector(Selectors.getBalance);
const spriteStyle = useSprite({
base: "MAIN_BALANCE_BACKGROUND",
thumb: "MAIN_BALANCE_THUMB",
activeThumb: "MAIN_BALANCE_THUMB_ACTIVE",
});
return (
<Balance
id="balance"
style={{ backgroundPosition: `0 -${offsetFromBalance(balance)}px` }}
style={{
...spriteStyle,
left: 177,
top: 57,
height: 13,
width: 38,
position: "absolute",
backgroundPosition: `0 -${offsetFromBalance(balance)}px`,
}}
/>
);
});
Expand Down
2 changes: 1 addition & 1 deletion packages/webamp/js/components/MainWindow/MainVolume.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const MainVolume = React.memo(() => {
};
return (
<div id="volume" style={style}>
<Volume />
<Volume style={{ background: "none" }} />
</div>
);
});
Expand Down
4 changes: 3 additions & 1 deletion packages/webamp/js/components/MainWindow/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import * as Selectors from "../../selectors";
import "../../../css/main-window.css";
import { FilePicker } from "../../types";
import FocusTarget from "../FocusTarget";
import { useActionCreator, useTypedSelector } from "../../hooks";
import { useActionCreator, useSprite, useTypedSelector } from "../../hooks";

interface Props {
analyser: AnalyserNode;
Expand All @@ -52,6 +52,7 @@ const MainWindow = React.memo(({ analyser, filePickers }: Props) => {
const doubled = useTypedSelector(Selectors.getDoubled);
const llama = useTypedSelector(Selectors.getLlamaMode);
const working = useTypedSelector(Selectors.getWorking);
const style = useSprite({ base: "MAIN_WINDOW_BACKGROUND" });

const className = classnames({
window: true,
Expand All @@ -74,6 +75,7 @@ const MainWindow = React.memo(({ analyser, filePickers }: Props) => {

return (
<DropTarget
style={style}
id="main-window"
windowId={WINDOWS.MAIN}
className={className}
Expand Down
102 changes: 68 additions & 34 deletions packages/webamp/js/components/Skin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { SkinImages } from "../types";
import { createSelector } from "reselect";
import Css from "./Css";
import ClipPaths from "./ClipPaths";
import { imageVarName, cursorVarName } from "../utils";

const CSS_PREFIX = "#webamp";

Expand All @@ -34,28 +35,37 @@ const FALLBACKS: { [key: string]: string } = {
MAIN_BALANCE_THUMB_ACTIVE: "MAIN_VOLUME_THUMB_SELECTED",
};

const getCssRules = createSelector(
const getCssVars = createSelector(
Selectors.getSkinImages,
Selectors.getSkinCursors,
Selectors.getSkinLetterWidths,
Selectors.getSkinRegion,
(skinImages, skinCursors, skinGenLetterWidths, skinRegion): string | null => {
(skinImages, skinCursors): string | null => {
if (!skinImages || !skinCursors) {
return null;
}
const cssRules = [];
Object.keys(imageSelectors).forEach((imageName) => {
const imageUrl =
skinImages[imageName] || skinImages[FALLBACKS[imageName]];
if (imageUrl) {
imageSelectors[imageName].forEach((selector) => {
cssRules.push(
`${CSS_PREFIX} ${selector} {background-image: url(${imageUrl})}`
);
});
}
});
const variableRules = [
...Object.entries(skinImages).map(([imageName, value]) => [
imageVarName(imageName),
value || skinImages[FALLBACKS[imageName]],
]),
...Object.entries(skinCursors).map(([imageName, value]) => [
cursorVarName(imageName),
value,
]),
];

const rules = variableRules
.filter(([, image]) => image != null)
.map(([name, image]) => `${name}: url(${image})`)
.join(";\n ");

return `${CSS_PREFIX} {\n ${rules}\n}`;
}
);

const getLetterWidths = createSelector(
Selectors.getSkinLetterWidths,
(skinGenLetterWidths) => {
const cssRules: string[] = [];
if (skinGenLetterWidths != null) {
LETTERS.forEach((letter) => {
const width = skinGenLetterWidths[`GEN_TEXT_${letter}`];
Expand All @@ -69,21 +79,43 @@ const getCssRules = createSelector(
);
});
}
return cssRules.join("\n");
}
);

const getCssRules = createSelector(
Selectors.getSkinImages,
Selectors.getSkinRegion,
(skinImages, skinRegion): string => {
const cssRules = [];
// DEPRECATED: All of these should be replaced with calls to `useSprite` in
// the component that actually renders the element.
Object.entries(imageSelectors).forEach(([imageName, selectors]) => {
selectors.forEach((selector) => {
// Ideally we would collapse this down into a single rule that has
// multipel comma separated selectors. However, `::-moz-range-thumb`
// will break the rule in non-Firefox browsers, so we just make each
// selector it's own rule.
cssRules.push(
`${CSS_PREFIX} ${selector} {\n background-image: var(${imageVarName(
imageName
)});\n}`
);
});
});

Object.keys(cursorSelectors).forEach((cursorName) => {
const cursorUrl = skinCursors[cursorName];
if (cursorUrl) {
cursorSelectors[cursorName].forEach((selector) => {
cssRules.push(
`${
// TODO: Fix this hack
// Maybe our CSS name spacing should be based on some other class/id
// than the one we use for defining the main div.
// That way it could be shared by both the player and the context menu.
selector.startsWith("#webamp-context-menu") ? "" : CSS_PREFIX
} ${selector} {cursor: url(${cursorUrl}), auto}`
);
});
}
cursorSelectors[cursorName].forEach((selector) => {
cssRules.push(
`${
// TODO: Fix this hack
// Maybe our CSS name spacing should be based on some other class/id
// than the one we use for defining the main div.
// That way it could be shared by both the player and the context menu.
selector.startsWith("#webamp-context-menu") ? "" : CSS_PREFIX
} ${selector} {cursor: var(${cursorVarName(cursorName)}), auto}`
);
});
});

if (numExIsUsed(skinImages)) {
Expand Down Expand Up @@ -119,13 +151,15 @@ const getClipPaths = createSelector(Selectors.getSkinRegion, (skinRegion) => {

export default function Skin() {
const cssRules = useTypedSelector(getCssRules);
const cssVars = useTypedSelector(getCssVars);
const letterWidths = useTypedSelector(getLetterWidths);
const clipPaths = useTypedSelector(getClipPaths);
if (cssRules == null) {
return null;
}
return (
<>
<Css id="webamp-skin">{cssRules}</Css>
<Css id="webamp-style">{cssRules}</Css>
{cssVars != null && (
<Css id="webamp-skin">{`${cssVars} ${letterWidths}`}</Css>
)}
<ClipPaths>{clipPaths}</ClipPaths>
</>
);
Expand Down