django-colorfield icon indicating copy to clipboard operation
django-colorfield copied to clipboard

Hide color-picker spectrum when using `choices`.

Open fabiocaccamo opened this issue 3 years ago • 7 comments

The continuation of #68

Fund with Polar

fabiocaccamo avatar Dec 23 '21 13:12 fabiocaccamo

This seems impossible to be done with the current jscolor library. I start to consider the idea to switch to another color picker with the same UX and a similar implementation.

Here the list of candidates:

fabiocaccamo avatar Dec 23 '21 14:12 fabiocaccamo

* [Coloris](https://github.com/mdbassit/Coloris)

That would be nice. I came here to inspect how hard it would be to add hsl support as I have my palette defined in that.

CharString avatar Feb 15 '22 13:02 CharString

Moving away from JSColor would be good as whilst this package is MIT Licensed JSColor is not. It's only GNU GPL v3 for opensource packages any commercial or even private projects would not be able to use this library due to their license.

StevenMapes avatar Aug 09 '22 07:08 StevenMapes

@StevenMapes I'm aware of this limitation, unfortunately at the moment I have not the time for working on it.

I created this discussion #83.

fabiocaccamo avatar Aug 09 '22 08:08 fabiocaccamo

Here is a workaround:

const startObserving = (domNode) => {
        const observer = new MutationObserver(mutations => {
            mutations.forEach(function (mutation) {
                const element = Array.from(mutation.addedNodes).filter(
                    element => {
                        if (element.classList) {
                            return element.classList.contains('jscolor-picker-wrap');
                        } else {
                            return false;
                        }
                    },
                );

                if (element.length) {
                    const wrapper = element[0]
                    const picker = document.querySelector('.jscolor-picker')
                    const palette = document.querySelector('.jscolor-palette')
                    document.querySelectorAll(".jscolor-picker > div:not(.jscolor-palette)").forEach((el) => picker.removeChild(el))
                    wrapper.style.height = `${wrapper.offsetHeight - palette.offsetTop + 20}px`
                    picker.style.height = `${picker.offsetHeight - palette.offsetTop + 20}px`
                    palette.style.top = `20px`
                }
            });
        });
         observer.observe(domNode, {
            childList: true,
            attributes: true,
            characterData: true,
            subtree: true,
        });

        return observer;
    };


startObserving(document.body, 'jscolor-picker'); // Replace the first parameter with the tag where the jscolor component is called.

Since the jscolor library destroys and recreates the HTML node everytime it's activated, we have to use the MutationObserver to listen for it's creation. This code listens to the HTML node where the jscolor is placed at and removes every child element (which includes the color picker and slider) except for the color palette. Then, it resizes the surrounding divs to shrink to exactly the height of the color palette.

Nicholas-Toh avatar Dec 07 '23 04:12 Nicholas-Toh

@Nicholas-Toh great workaround, thanks for sharing it!

Considering how JSColor creates/destroys the element I think that this is the only possible solution.

fabiocaccamo avatar Dec 07 '23 06:12 fabiocaccamo

@Nicholas-Toh I tested your snippet is an admin page with many colorfields because I wanted to implement it, but it seems to have many issues:

  • it ignores the fact that it should work only on fields that are using the choices options.
  • it doesn't work correctly depending on the position where the color-picker appears

Here an improved version of your script in case you would like to adjust it / submit a PR:

document.addEventListener('DOMContentLoaded', function () {
    const startObserving = (domNode) => {
        const observer = new MutationObserver(mutations => {
            mutations.forEach(function (mutation) {
                const element = Array.from(mutation.addedNodes).find(
                    element => element.classList && element.classList.contains('jscolor-picker-wrap')
                );
                if (element) {
                    const wrapper = element;
                    const picker = document.querySelector('.jscolor-picker');
                    const palette = document.querySelector('.jscolor-palette');
                    
                    document.querySelectorAll(".jscolor-picker > div:not(.jscolor-palette)").forEach((el) => el.remove());
                    
                    const newHeight = wrapper.offsetHeight - palette.offsetTop + 20;
                    wrapper.style.height = `${newHeight}px`;
                    picker.style.height = `${newHeight}px`;
                    palette.style.top = '20px';
                }
            });
        });
        observer.observe(domNode, {
            childList: true,
            attributes: true,
            characterData: true,
            subtree: true,
        });
        return observer;
    };
    startObserving(document.body);
});

fabiocaccamo avatar Jan 29 '24 17:01 fabiocaccamo