Skip to content

Commit

Permalink
fix: Create CSS vars for SVG patterns.
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnesky committed Nov 23, 2024
1 parent af5905a commit b78ec91
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 56 deletions.
6 changes: 1 addition & 5 deletions core/bubbles/bubble.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,7 @@ export abstract class Bubble implements IBubble, ISelectable {
);
const embossGroup = dom.createSvgElement(
Svg.G,
{
'filter': `url(#${
this.workspace.getRenderer().getConstants().embossFilterId
})`,
},
{'class': 'blocklyEmboss'},
this.svgRoot,
);
this.tail = dom.createSvgElement(
Expand Down
9 changes: 9 additions & 0 deletions core/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ let content = `
transition: transform .5s;
}
.blocklyEmboss {
filter: var(--blocklyEmbossFilter);
}
.blocklyTooltipDiv {
background-color: #ffffc7;
border: 1px solid #ddc;
Expand Down Expand Up @@ -138,6 +142,10 @@ let content = `
border-color: inherit;
}
.blocklyHighlighted>.blocklyPath {
filter: var(--blocklyEmbossFilter);
}
.blocklyHighlightedConnectionPath {
fill: none;
stroke: #fc3;
Expand Down Expand Up @@ -189,6 +197,7 @@ let content = `
}
.blocklyDisabled>.blocklyPath {
fill: var(--blocklyDisabledPattern);
fill-opacity: .5;
stroke-opacity: .5;
}
Expand Down
15 changes: 15 additions & 0 deletions core/grid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,17 @@ export class Grid {
* @param rnd A random ID to append to the pattern's ID.
* @param gridOptions The object containing grid configuration.
* @param defs The root SVG element for this workspace's defs.
* @param injectionDiv The div containing the parent workspace and all related
* workspaces and block containers. CSS variables representing SVG patterns
* will be scoped to this container.
* @returns The SVG element for the grid pattern.
* @internal
*/
static createDom(
rnd: string,
gridOptions: GridOptions,
defs: SVGElement,
injectionDiv?: HTMLElement,
): SVGElement {
/*
<pattern id="blocklyGridPattern837493" patternUnits="userSpaceOnUse">
Expand Down Expand Up @@ -247,6 +251,17 @@ export class Grid {
// Edge 16 doesn't handle empty patterns
dom.createSvgElement(Svg.LINE, {}, gridPattern);
}

if (injectionDiv) {
// Add CSS variables scoped to the injection div referencing the created
// patterns so that CSS can apply the patterns to any element in the
// injection div.
injectionDiv.style.setProperty(
'--blocklyGridPattern',
`url(#${gridPattern.id})`,
);
}

return gridPattern;
}
}
11 changes: 8 additions & 3 deletions core/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export function inject(
* @param options Dictionary of options.
* @returns Newly created SVG image.
*/
function createDom(container: Element, options: Options): SVGElement {
function createDom(container: HTMLElement, options: Options): SVGElement {
// Sadly browsers (Chrome vs Firefox) are currently inconsistent in laying
// out content in RTL mode. Therefore Blockly forces the use of LTR,
// then manually positions content in RTL as needed.
Expand Down Expand Up @@ -132,7 +132,12 @@ function createDom(container: Element, options: Options): SVGElement {
// https://neil.fraser.name/news/2015/11/01/
const rnd = String(Math.random()).substring(2);

options.gridPattern = Grid.createDom(rnd, options.gridOptions, defs);
options.gridPattern = Grid.createDom(
rnd,
options.gridOptions,
defs,
container,
);
return svg;
}

Expand All @@ -144,7 +149,7 @@ function createDom(container: Element, options: Options): SVGElement {
* @returns Newly created main workspace.
*/
function createMainWorkspace(
injectionDiv: Element,
injectionDiv: HTMLElement,
svg: SVGElement,
options: Options,
): WorkspaceSvg {
Expand Down
30 changes: 29 additions & 1 deletion core/renderers/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -926,8 +926,18 @@ export class ConstantProvider {
* @param svg The root of the workspace's SVG.
* @param tagName The name to use for the CSS style tag.
* @param selector The CSS selector to use.
* @param injectionDivIfIsParent The div containing the parent workspace and
* all related workspaces and block containers, if this renderer is for the
* parent workspace. CSS variables representing SVG patterns will be scoped
* to this container. Child workspaces should not override the CSS variables
* created by the parent and thus do not need access to the injection div.
*/
createDom(svg: SVGElement, tagName: string, selector: string) {
createDom(
svg: SVGElement,
tagName: string,
selector: string,
injectionDivIfIsParent?: HTMLElement,
) {
this.injectCSS_(tagName, selector);

/*
Expand Down Expand Up @@ -1034,6 +1044,24 @@ export class ConstantProvider {
this.disabledPattern = disabledPattern;

this.createDebugFilter();

if (injectionDivIfIsParent) {
// If this renderer is for the parent workspace, add CSS variables scoped
// to the injection div referencing the created patterns so that CSS can
// apply the patterns to any element in the injection div.
injectionDivIfIsParent.style.setProperty(
'--blocklyEmbossFilter',
`url(#${this.embossFilterId})`,
);
injectionDivIfIsParent.style.setProperty(
'--blocklyDisabledPattern',
`url(#${this.disabledPatternId})`,
);
injectionDivIfIsParent.style.setProperty(
'--blocklyDebugFilter',
`url(#${this.debugFilterId})`,
);
}
}

/**
Expand Down
11 changes: 0 additions & 11 deletions core/renderers/common/path_object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,8 @@ export class PathObject implements IPathObject {

updateHighlighted(enable: boolean) {
if (enable) {
this.svgPath.setAttribute(
'filter',
'url(#' + this.constants.embossFilterId + ')',
);
this.setClass_('blocklyHighlighted', true);
} else {
this.svgPath.setAttribute('filter', 'none');
this.setClass_('blocklyHighlighted', false);
}
}
Expand All @@ -206,12 +201,6 @@ export class PathObject implements IPathObject {
*/
protected updateDisabled_(disabled: boolean) {
this.setClass_('blocklyDisabled', disabled);
if (disabled) {
this.svgPath.setAttribute(
'fill',
'url(#' + this.constants.disabledPatternId + ')',
);
}
}

/**
Expand Down
27 changes: 23 additions & 4 deletions core/renderers/common/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,23 @@ export class Renderer implements IRegistrable {
*
* @param svg The root of the workspace's SVG.
* @param theme The workspace theme object.
* @param injectionDivIfIsParent The div containing the parent workspace and
* all related workspaces and block containers, if this renderer is for the
* parent workspace. CSS variables representing SVG patterns will be scoped
* to this container. Child workspaces should not override the CSS variables
* created by the parent and thus do not need access to the injection div.
* @internal
*/
createDom(svg: SVGElement, theme: Theme) {
createDom(
svg: SVGElement,
theme: Theme,
injectionDivIfIsParent?: HTMLElement,
) {
this.constants_.createDom(
svg,
this.name + '-' + theme.name,
'.' + this.getClassName() + '.' + theme.getClassName(),
injectionDivIfIsParent,
);
}

Expand All @@ -93,8 +103,17 @@ export class Renderer implements IRegistrable {
*
* @param svg The root of the workspace's SVG.
* @param theme The workspace theme object.
*/
refreshDom(svg: SVGElement, theme: Theme) {
* @param injectionDivIfIsParent The div containing the parent workspace and
* all related workspaces and block containers, if this renderer is for the
* parent workspace. CSS variables representing SVG patterns will be scoped
* to this container. Child workspaces should not override the CSS variables
* created by the parent and thus do not need access to the injection div.
*/
refreshDom(
svg: SVGElement,
theme: Theme,
injectionDivIfIsParent?: HTMLElement,
) {
const previousConstants = this.getConstants();
previousConstants.dispose();
this.constants_ = this.makeConstants_();
Expand All @@ -105,7 +124,7 @@ export class Renderer implements IRegistrable {
this.constants_.randomIdentifier = previousConstants.randomIdentifier;
this.constants_.setTheme(theme);
this.constants_.init();
this.createDom(svg, theme);
this.createDom(svg, theme, injectionDivIfIsParent);
}

/**
Expand Down
8 changes: 6 additions & 2 deletions core/renderers/geras/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,12 @@ export class Renderer extends BaseRenderer {
this.highlightConstants.init();
}

override refreshDom(svg: SVGElement, theme: Theme) {
super.refreshDom(svg, theme);
override refreshDom(
svg: SVGElement,
theme: Theme,
injectionDiv: HTMLElement,
) {
super.refreshDom(svg, theme, injectionDiv);
this.getHighlightConstants().init();
}

Expand Down
34 changes: 31 additions & 3 deletions core/renderers/zelos/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -675,8 +675,13 @@ export class ConstantProvider extends BaseConstantProvider {
return utilsColour.blend('#000', colour, 0.25) || colour;
}

override createDom(svg: SVGElement, tagName: string, selector: string) {
super.createDom(svg, tagName, selector);
override createDom(
svg: SVGElement,
tagName: string,
selector: string,
injectionDivIfIsParent?: HTMLElement,
) {
super.createDom(svg, tagName, selector, injectionDivIfIsParent);
/*
<defs>
... filters go here ...
Expand Down Expand Up @@ -795,6 +800,20 @@ export class ConstantProvider extends BaseConstantProvider {
);
this.replacementGlowFilterId = replacementGlowFilter.id;
this.replacementGlowFilter = replacementGlowFilter;

if (injectionDivIfIsParent) {
// If this renderer is for the parent workspace, add CSS variables scoped
// to the injection div referencing the created patterns so that CSS can
// apply the patterns to any element in the injection div.
injectionDivIfIsParent.style.setProperty(
'--blocklySelectedGlowFilter',
`url(#${this.selectedGlowFilterId})`,
);
injectionDivIfIsParent.style.setProperty(
'--blocklyReplacementGlowFilter',
`url(#${this.replacementGlowFilterId})`,
);
}
}

override getCSS_(selector: string) {
Expand Down Expand Up @@ -873,14 +892,23 @@ export class ConstantProvider extends BaseConstantProvider {

// Disabled outline paths.
`${selector} .blocklyDisabled > .blocklyOutlinePath {`,
`fill: url(#blocklyDisabledPattern${this.randomIdentifier})`,
`fill: var(--blocklyDisabledPattern)`,
`}`,

// Insertion marker.
`${selector} .blocklyInsertionMarker>.blocklyPath {`,
`fill-opacity: ${this.INSERTION_MARKER_OPACITY};`,
`stroke: none;`,
`}`,

`${selector} .blocklySelected>.blocklyPath.blocklyPathSelected {`,
`fill: none;`,
`filter: var(--blocklySelectedGlowFilter);`,
`}`,

`${selector} .blocklyReplaceable>.blocklyPath {`,
`filter: var(--blocklyReplacementGlowFilter);`,
`}`,
];
}
}
Expand Down
14 changes: 1 addition & 13 deletions core/renderers/zelos/path_object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,7 @@ export class PathObject extends BasePathObject {
if (enable) {
if (!this.svgPathSelected) {
this.svgPathSelected = this.svgPath.cloneNode(true) as SVGElement;
this.svgPathSelected.setAttribute('fill', 'none');
this.svgPathSelected.setAttribute(
'filter',
'url(#' + this.constants.selectedGlowFilterId + ')',
);
this.svgPathSelected.classList.add('blocklyPathSelected');
this.svgRoot.appendChild(this.svgPathSelected);
}
} else {
Expand All @@ -108,14 +104,6 @@ export class PathObject extends BasePathObject {

override updateReplacementFade(enable: boolean) {
this.setClass_('blocklyReplaceable', enable);
if (enable) {
this.svgPath.setAttribute(
'filter',
'url(#' + this.constants.replacementGlowFilterId + ')',
);
} else {
this.svgPath.removeAttribute('filter');
}
}

override updateShapeForInputHighlight(conn: Connection, enable: boolean) {
Expand Down
Loading

0 comments on commit b78ec91

Please sign in to comment.