org-chart icon indicating copy to clipboard operation
org-chart copied to clipboard

Examples => Safari Issue

Open bumbeishvili opened this issue 3 years ago • 13 comments

Safari is unable to correctly render foreignObject elements, where content is overflowing.

And it leads to weird issues.

First two examples should be free of this issue, since I made sure that they would not overflow specified dimensions (so it should be safe to reuse in case safari support is important)

bumbeishvili avatar Aug 12 '21 11:08 bumbeishvili

Hello @bumbeishvili , by when do you plan to fix the issues with Safari browser?

mishra-prachi avatar Sep 07 '21 12:09 mishra-prachi

To be honest I hope safari will fix that issue. I don't plan to make changes to existing examples.

I made the first two examples of Safari safe. Meaning they will work in Safari, but other examples only work in Chrome/Edge/Firefox

bumbeishvili avatar Sep 07 '21 12:09 bumbeishvili

Hey @bumbeishvili This package is awesome btw! Do you have any idea why it's not working in Safari (to build a workaround if necessary)? I find the expand/collapse button isn't working even on the first two examples. Seems like the click event is not being registerred.

kmcgrady avatar Sep 09 '21 04:09 kmcgrady

@kmcgrady Main issue is that safari is unable to correctly render foreignObject element, when content is overflowing.

In chrome overflow:visible works well for foreignObject and it's possible to create very customized node content because of that. But in Safari, it leads to weird issues.

I made sure that in the first two examples, content would not overflow to specified node dimensions and they were seemed to be working in safari well.

As for the click event, this was not happening before. I've created a new Github issue and will probably fix it in the following days.

https://github.com/bumbeishvili/org-chart/issues/86

bumbeishvili avatar Sep 09 '21 05:09 bumbeishvili

Update: - expand & collapse button should be working for the first two (safari proof) examples now

bumbeishvili avatar Dec 06 '21 21:12 bumbeishvili

I had similar issue (https://github.com/bumbeishvili/org-chart/issues/120) with this. Tweaked the code a little and it worked for me. I hope it helps you too.

https://stackblitz.com/edit/web-platform-xkdtpd

n1crack avatar Feb 01 '22 19:02 n1crack

@bumbeishvili thank you for such a great library!

I ran into two issues in Safari related due to overflowing content in the foreignobject:

  • Dragging issues : already highlighted
  • Content that was using position was displayed to the top left of the page : already captured in this issue and https://github.com/bumbeishvili/org-chart/issues/120

I was able to get around those issues by making sure all content was in foreignobject and now I'm wondering if it would be possible to extend the connecting lines with linkUpdate?

Here are some screenshots to hopefully clarify:

image

image

Hoping to extend the line to touch the white background ⬆️

jd-gray avatar Mar 18 '22 17:03 jd-gray

@jd-gray in your place I'd have three options:

  1. Just move the node to the without going outside foreignObject element? Not sure if issues still appear after that. Also as I see you are using drop shadow. I have to warn you that it will have horrible performance hit

  2. If I remember correctly, depending on your layout type, this line is responsible for line's x position (again, not sure, spent so much time debugging it )

https://github.com/bumbeishvili/org-chart/blob/3b7cc9d332d657ed5e710631abaad36dfc5ebe0f/src/d3-org-chart.js#L128

Unfortunately, you can't override this particular piece, you'll need to copy everything under layout bindings and then change only specific ones (If you are not using other layouts, you can drop some of them)

So, it will be like this roughly:

chart.layoutBindings(
{
                "left": {
                    "nodeLeftX": node => 0,
                    "nodeRightX": node => node.width,
                    "nodeTopY": node => - node.height / 2,
                    "nodeBottomY": node => node.height / 2,
                    "nodeJoinX": node => node.x + node.width,
                    "nodeJoinY": node => node.y - node.height / 2,
                    "linkJoinX": node => node.x + node.width,   // Modify this line, like node.x - I think this is the correct property, I'd try all link related properties
                    "linkJoinY": node => node.y,
                    "linkX": node => node.x,
                    "linkY": node => node.y,
                    "linkCompactXStart": node => node.x + node.width / 2,//node.x + (node.compactEven ? node.width / 2 : -node.width / 2),
                    "linkCompactYStart": node => node.y + (node.compactEven ? node.height / 2 : -node.height / 2),
                    "compactLinkMidX": (node, state) => node.firstCompactNode.x,// node.firstCompactNode.x + node.firstCompactNode.flexCompactDim[0] / 4 + state.compactMarginPair(node) / 4,
                    "compactLinkMidY": (node, state) => node.firstCompactNode.y + node.firstCompactNode.flexCompactDim[0] / 4 + state.compactMarginPair(node) / 4,
                    "linkParentX": node => node.parent.x + node.parent.width,
                    "linkParentY": node => node.parent.y,
                    "buttonX": node => node.width,
                    "buttonY": node => node.height / 2,
                    "centerTransform": ({ root, rootMargin, centerY, scale, centerX }) => `translate(${rootMargin},${centerY}) scale(${scale})`,
                    "compactDimension": {
                        sizeColumn: node => node.height,
                        sizeRow: node => node.width,
                        reverse: arr => arr.slice().reverse()
                    },
                    "nodeFlexSize": ({ height, width, siblingsMargin, childrenMargin, state, node }) => {
                        if (state.compact && node.flexCompactDim) {
                            const result = [node.flexCompactDim[0], node.flexCompactDim[1]]
                            return result;
                        };
                        return [height + siblingsMargin, width + childrenMargin]
                    },
                    "zoomTransform": ({ centerY, scale }) => `translate(${0},${centerY}) scale(${scale})`,
                    "diagonal": this.hdiagonal.bind(this),
                    "swap": d => { const x = d.x; d.x = d.y; d.y = x; },
                    "nodeUpdateTransform": ({ x, y, width, height }) => `translate(${x},${y - height / 2})`,
                },
                "top": {
                    "nodeLeftX": node => -node.width / 2,
                    "nodeRightX": node => node.width / 2,
                    "nodeTopY": node => 0,
                    "nodeBottomY": node => node.height,
                    "nodeJoinX": node => node.x - node.width / 2,
                    "nodeJoinY": node => node.y + node.height,
                    "linkJoinX": node => node.x, 
                    "linkJoinY": node => node.y + node.height,
                    "linkCompactXStart": node => node.x + (node.compactEven ? node.width / 2 : -node.width / 2),
                    "linkCompactYStart": node => node.y + node.height / 2,
                    "compactLinkMidX": (node, state) => node.firstCompactNode.x + node.firstCompactNode.flexCompactDim[0] / 4 + state.compactMarginPair(node) / 4,
                    "compactLinkMidY": node => node.firstCompactNode.y,
                    "compactDimension": {
                        sizeColumn: node => node.width,
                        sizeRow: node => node.height,
                        reverse: arr => arr,
                    },
                    "linkX": node => node.x,
                    "linkY": node => node.y,
                    "linkParentX": node => node.parent.x,
                    "linkParentY": node => node.parent.y + node.parent.height,
                    "buttonX": node => node.width / 2,
                    "buttonY": node => node.height,
                    "centerTransform": ({ root, rootMargin, centerY, scale, centerX }) => `translate(${centerX},${rootMargin}) scale(${scale})`,
                    "nodeFlexSize": ({ height, width, siblingsMargin, childrenMargin, state, node, compactViewIndex }) => {
                        if (state.compact && node.flexCompactDim) {
                            const result = [node.flexCompactDim[0], node.flexCompactDim[1]]
                            return result;
                        };
                        return [width + siblingsMargin, height + childrenMargin];
                    },
                    "zoomTransform": ({ centerX, scale }) => `translate(${centerX},0}) scale(${scale})`,
                    "diagonal": this.diagonal.bind(this),
                    "swap": d => { },
                    "nodeUpdateTransform": ({ x, y, width, height }) => `translate(${x - width / 2},${y})`,

                },
                "bottom": {
                    "nodeLeftX": node => -node.width / 2,
                    "nodeRightX": node => node.width / 2,
                    "nodeTopY": node => -node.height,
                    "nodeBottomY": node => 0,
                    "nodeJoinX": node => node.x - node.width / 2,
                    "nodeJoinY": node => node.y - node.height - node.height,
                    "linkJoinX": node => node.x,
                    "linkJoinY": node => node.y - node.height,
                    "linkCompactXStart": node => node.x + (node.compactEven ? node.width / 2 : -node.width / 2),
                    "linkCompactYStart": node => node.y - node.height / 2,
                    "compactLinkMidX": (node, state) => node.firstCompactNode.x + node.firstCompactNode.flexCompactDim[0] / 4 + state.compactMarginPair(node) / 4,
                    "compactLinkMidY": node => node.firstCompactNode.y,
                    "linkX": node => node.x,
                    "linkY": node => node.y,
                    "compactDimension": {
                        sizeColumn: node => node.width,
                        sizeRow: node => node.height,
                        reverse: arr => arr,
                    },
                    "linkParentX": node => node.parent.x,
                    "linkParentY": node => node.parent.y - node.parent.height,
                    "buttonX": node => node.width / 2,
                    "buttonY": node => 0,
                    "centerTransform": ({ root, rootMargin, centerY, scale, centerX, chartHeight }) => `translate(${centerX},${chartHeight - rootMargin}) scale(${scale})`,
                    "nodeFlexSize": ({ height, width, siblingsMargin, childrenMargin, state, node }) => {
                        if (state.compact && node.flexCompactDim) {
                            const result = [node.flexCompactDim[0], node.flexCompactDim[1]]
                            return result;
                        };
                        return [width + siblingsMargin, height + childrenMargin]
                    },
                    "zoomTransform": ({ centerX, scale }) => `translate(${centerX},0}) scale(${scale})`,
                    "diagonal": this.diagonal.bind(this),
                    "swap": d => { d.y = -d.y; },
                    "nodeUpdateTransform": ({ x, y, width, height }) => `translate(${x - width / 2},${y - height})`,
                },
                "right": {
                    "nodeLeftX": node => -node.width,
                    "nodeRightX": node => 0,
                    "nodeTopY": node => - node.height / 2,
                    "nodeBottomY": node => node.height / 2,
                    "nodeJoinX": node => node.x - node.width - node.width,
                    "nodeJoinY": node => node.y - node.height / 2,
                    "linkJoinX": node => node.x - node.width,
                    "linkJoinY": node => node.y,
                    "linkX": node => node.x,
                    "linkY": node => node.y,
                    "linkParentX": node => node.parent.x - node.parent.width,
                    "linkParentY": node => node.parent.y,
                    "buttonX": node => 0,
                    "buttonY": node => node.height / 2,
                    "linkCompactXStart": node => node.x - node.width / 2,//node.x + (node.compactEven ? node.width / 2 : -node.width / 2),
                    "linkCompactYStart": node => node.y + (node.compactEven ? node.height / 2 : -node.height / 2),
                    "compactLinkMidX": (node, state) => node.firstCompactNode.x,// node.firstCompactNode.x + node.firstCompactNode.flexCompactDim[0] / 4 + state.compactMarginPair(node) / 4,
                    "compactLinkMidY": (node, state) => node.firstCompactNode.y + node.firstCompactNode.flexCompactDim[0] / 4 + state.compactMarginPair(node) / 4,
                    "centerTransform": ({ root, rootMargin, centerY, scale, centerX, chartWidth }) => `translate(${chartWidth - rootMargin},${centerY}) scale(${scale})`,
                    "nodeFlexSize": ({ height, width, siblingsMargin, childrenMargin, state, node }) => {
                        if (state.compact && node.flexCompactDim) {
                            const result = [node.flexCompactDim[0], node.flexCompactDim[1]]
                            return result;
                        };
                        return [height + siblingsMargin, width + childrenMargin]
                    },
                    "compactDimension": {
                        sizeColumn: node => node.height,
                        sizeRow: node => node.width,
                        reverse: arr => arr.slice().reverse()
                    },
                    "zoomTransform": ({ centerY, scale }) => `translate(${0},${centerY}) scale(${scale})`,
                    "diagonal": this.hdiagonal.bind(this),
                    "swap": d => { const x = d.x; d.x = -d.y; d.y = x; },
                    "nodeUpdateTransform": ({ x, y, width, height }) => `translate(${x - width},${y - height / 2})`,
                },
            }

})
  1. Just copy the code here and use it locally and modify as you want. It's just a one class

https://github.com/bumbeishvili/org-chart/blob/3b7cc9d332d657ed5e710631abaad36dfc5ebe0f/src/d3-org-chart.js

bumbeishvili avatar Mar 18 '22 19:03 bumbeishvili

Thank you @bumbeishvili, I'll give those suggestions a try!

jd-gray avatar Mar 18 '22 23:03 jd-gray

@bumbeishvili as a heads up I was able to fix my issue by updating a few properties and spread the properties instead of pasting the entire layoutBindings. Again thank you for your suggestions!

      const yOverride = 18;
      const innerCardWidth = 104;

      chart.layoutBindings({
        ...chart.layoutBindings(),
        top: {
          ...chart.layoutBindings().top,
          linkY: (node: HierarchyNodeD3) => node.y + yOverride,
          compactLinkMidY: (node: HierarchyNodeD3) => node.firstCompactNode.y + yOverride,
          linkCompactXStart: (node: HierarchyNodeD3) =>
            node.x + (node.compactEven ? innerCardWidth / 2 : -innerCardWidth / 2),
          buttonY: (node) => node.height - 10,
        },
      });

jd-gray avatar Mar 22 '22 22:03 jd-gray

Good to know

bumbeishvili avatar Mar 23 '22 06:03 bumbeishvili

Hi @bumbeishvili I'm getting this issue using shadows. Do you know how can I fix it?

image

leoalipazaga avatar Sep 08 '22 16:09 leoalipazaga

I don't know right away, I guess it requires trial and errors. I won't recommend using shadows generally, it noticeably slows org chart down

bumbeishvili avatar Sep 08 '22 16:09 bumbeishvili

hello @bumbeishvili and others! I'm trying to pdate my diagram to work also in Safari. I read this thread and also the other related questions and I'm stuck as I'm not able to pinpoint which combination of styles is causing my diagram to break in Safari. I was hoping you could share some specific tips on how to troubleshoot the issues? For example, any recommendation of display and sizes?

image

haschdl avatar Oct 27 '22 18:10 haschdl

hello again @bumbeishvili I found out that my issues are due to hard-coded sizes for the foreingObject and rect for the button part. The code has a hard-coded 40x40 pixels. Is there a way to change that, which is not changing the source code? I found a horrible hack to update the rectand foreignObject at every nodeUpdate, but that can't be the right way.

chart.nodeUpdate((e, d) => {
        maxY = Math.max(maxY, e.y, window.innerHeight);
        const buttonNodes = document.getElementsByClassName('node-button-foreign-object');
        for (const node of buttonNodes) {
            node.setAttribute("width", 120)
            node.setAttribute("x", -60)
        }

        const buttonRectNodes = document.getElementsByClassName('node-button-rect');
        for (const node of buttonRectNodes)
            node.setAttribute("width", 120)
    });

haschdl avatar Oct 28 '22 08:10 haschdl

Hi, this issue also bit me in another project, and I made some modifications in that part, but could not find a time to apply changes here as well.

Your solution can become a better hack of you try to use 'this', which refers to current node DOM element. Don't forget converting arrow function to the normal function

So, just replace the document with 'this' and it should work still.

bumbeishvili avatar Oct 28 '22 19:10 bumbeishvili