Skip to content

Commit 8a041e1

Browse files
Yair Even OrYair Even Or
Yair Even Or
authored and
Yair Even Or
committed
improved demo so color-pickers are showing with an animation, and are removed from the DOM when aren't needed anymore (click-outside)
1 parent 95190b0 commit 8a041e1

File tree

1 file changed

+70
-58
lines changed

1 file changed

+70
-58
lines changed

index.html

+70-58
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,6 @@
7272
}
7373
}
7474

75-
.color-picker.hidden{
76-
opacity: 0;
77-
pointer-events: none;
78-
}
79-
8075
.color-picker[positioned]{
8176
--x: calc(var(--pos-left) + var(--window-scroll-x));
8277
--y: calc(var(--pos-top) + var(--window-scroll-y));
@@ -89,13 +84,23 @@
8984
background: white;
9085
top: 0;
9186
left: 0;
87+
animation: .15s reveal-picker ease-out forwards;
9288
transform: translate(calc(var(--x) * 1px),
93-
calc(var(--y) * 1px));
89+
calc(var(--y) * 1px));
9490

9591
@media only screen and (max-device-width : 640px) {
9692
max-width: 70%;
9793
}
9894
}
95+
96+
.color-picker[data-placement~=left]{ --left: 15px; }
97+
.color-picker[data-placement~=right]{ --left: -15px; }
98+
.color-picker[data-placement~=below]{ --top: -15px; }
99+
.color-picker[data-placement~=above]{ --top: 15px; }
100+
101+
@keyframes reveal-picker {
102+
from { opacity: 0; top: var(--top, 0); left: var(--left, 0); }
103+
}
99104
</style>
100105
</head>
101106
<body>
@@ -110,85 +115,92 @@ <h1>Color Picker</h1>
110115
<input class='myColor' inputmode='none' value='HSLA(244, 64%, 62%, 100%)' style='--color:HSLA(244, 64%, 62%, 100%)' data-swatches='["LightCoral", "MediumSeaGreen", "#3680FA99"]' data-placement='center below'/>
111116
<input class='myColor' inputmode='none' value='RGBA(255,0,170,.30' style='--color:rgba(255,0,170,.3)' data-swatches='["rgba(50,250,80,.5)"]' data-placement='right center' />
112117
</div>
113-
<script>
114-
/* https://www.npmjs.com/package/@yaireo/position */
115-
(function(a,b){if("function"==typeof define&&define.amd)define(["exports"],b);else if("undefined"!=typeof exports)b(exports);else{var c={exports:{}};b(c.exports),a.position=c.exports}})("undefined"==typeof globalThis?"undefined"==typeof self?this:self:globalThis,function(a){"use strict";var b=Math.round;Object.defineProperty(a,"__esModule",{value:!0}),a.default=void 0;/**
116-
* positions a DOM element next to a certain position
117-
* @param {HTMLElement} target DOM element node
118-
* @param {Object} ref node reference for positioning or just {x,y} coordinates
119-
* @param {String} placement [above/below/center & left/right/center] or mix of two (only works if "ref" is an HTML Element)
120-
* @param {Array} prevPlacement used when calculated new position overflows
121-
* @param {Array} offset distance (in pixels) from original placement position ("10px 20px" or just "10px" for both horizontal & vertical)
122-
*/const c=a=>{var{target:e,ref:f,offset:g,placement:h,prevPlacement:i,useRaf:j=!0}=a,k={x:f.x,y:f.y},l=f&&f.x?{...f}:{},m=document.documentElement,n={w:m.clientWidth,h:m.clientHeight},o={w:e.clientWidth,h:e.clientHeight};d=j?d:a=>a(),i=i||[],h=(h||" ").split(" ").map((b,a)=>b?b:["center","below"][a]),g=g?[g[0]||0,g[1]||g[0]||0]:[0,0],f instanceof Element&&(l=f.getBoundingClientRect(),k.x=l.x,k.y=l.y,k.w=l.width,k.h=l.height,"left"==h[0]?k.x-=o.w+g[0]:"right"==h[0]?k.x+=k.w+g[0]:k.x-=o.w/2-k.w/2,"above"==h[1]?k.y-=o.h+g[1]:"below"==h[1]?k.y+=k.h+g[1]:k.y-=o.h/2-k.h/2);const p={top:0>k.y,bottom:k.y+o.h>n.h,left:0>k.x,right:k.x+o.w>n.w},q=b=>c({...a,placement:b.join(" "),prevPlacement:h});// horizontal fix for overflows
123-
return p.left&&"right"!=i[0]?q(["right",h[1]]):p.right&&"left"!=i[0]?q(["left",h[1]]):p.bottom&&"above"!=i[1]?q([h[0],"above"]):p.top&&"below"!=i[1]?q([h[0],"below"]):(d(()=>{e.setAttribute("positioned",!0),e.setAttribute("data-placement",h.join(" ")),e.setAttribute("data-pos-overflow",Object.entries(p).reduce((a,[b,c])=>c?`${a} ${b}`:a,"").trim()),[["pos-left",k.x],// overflow.right ? vpSize.w - targetSize.w : pos.x
124-
["pos-top",k.y],// pos.y > offset[1] ? pos.y : 0
125-
["pos-target-width",o.w],["pos-target-height",o.h],["pos-ref-width",l.width||0],["pos-ref-height",l.height||0],["pos-ref-left",l.x],["pos-ref-top",l.y],["window-scroll-y",window.scrollY],["window-scroll-x",window.scrollX]].forEach(([a,c])=>e.style.setProperty("--"+a,b(c)))}),{pos:k,placement:h});// vertical fix for overflows
126-
// update target's position
127-
};let d=requestAnimationFrame||(a=>setTimeout(a,1e3/60));a.default=c});
128-
</script>
129118

130-
<script src="./dist/color-picker.js"></script>
119+
<script src='https://cdn.jsdelivr.net/npm/@yaireo/position'></script>
120+
<script src='./dist/color-picker.js'></script>
131121

132122
<script>
123+
// because "@yaireo/position" is used (in this demo) as a script file and not an node module (ES export)
133124
position = position.default;
125+
126+
// get the ColorPicker & some helper functions for color format transformations
134127
const { default:ColorPicker, any_to_hex, changeColorFormat } = window.ColorPicker
135128

136129
// let the delayed (non-blocking) CSS a chance to load first
137130
setTimeout(init, 200)
138131

139132
function init(){
140-
// iterate all color inputs and instantiate an new ColorPicker instance for each
133+
// iterate all color inputs and instantiate new ColorPicker instances (for each one)
141134
document.querySelectorAll('.myColor').forEach(colorInput => {
142-
const observerCallback = (entries) => {
143-
!cPicker.DOM.scope.classList.contains('hidden') &&
144-
position({ target:cPicker.DOM.scope, ref:colorInput, placement:colorInput.dataset.placement || 'center above', offset:[20] });
145-
}
146-
const resizeObserver = new ResizeObserver(observerCallback)
147-
const intersectionObserver = new IntersectionObserver(observerCallback, {root:document, threshold:1});
135+
const observerCallback = (entries) => {
136+
!cPicker.DOM.scope.classList.contains('hidden') &&
137+
position({ target:cPicker.DOM.scope, ref:colorInput, placement:colorInput.dataset.placement || 'center above', offset:[20] });
138+
}
139+
const resizeObserver = new ResizeObserver(observerCallback)
140+
const intersectionObserver = new IntersectionObserver(observerCallback, {root:document, threshold:1});
148141

149-
const cPicker = new ColorPicker({
150-
color: colorInput.value, // accepts formats: HEX(A), RGB(A), HSL(A)
142+
const cPicker = new ColorPicker({
143+
color: colorInput.value, // accepts formats: HEX(A), RGB(A), HSL(A)
151144

152-
className: 'hidden',
145+
defaultFormat: 'hsla',
153146

154-
defaultFormat: 'hsla',
147+
swatches: colorInput.dataset.swatches == 'false'
148+
? false
149+
: JSON.parse(colorInput.dataset.swatches),
155150

156-
swatches: colorInput.dataset.swatches == 'false'
157-
? false
158-
: JSON.parse(colorInput.dataset.swatches),
151+
swatchesLocalStorage: true,
159152

160-
swatchesLocalStorage: true,
153+
// when clicking anywhere that is not within the color picker.
154+
// use special logic if clicked on the color-input which is
155+
// assosiacated with this specific picker
156+
onClickOutside(e){
157+
let showPicker = false,
158+
isTargetColorInput = e.target == colorInput;
161159

162-
onClickOutside(e){
163-
let action = 'add',
164-
isTargetColorInput = e.target == colorInput
160+
const pickerElem = cPicker.DOM.scope;
165161

166-
if( isTargetColorInput ) action = 'toggle'
167-
if( e.key == 'Escape' ) action = 'add'
162+
if( isTargetColorInput ) showPicker = true
163+
if( e.key == 'Escape' ) showPicker = false
168164

169-
cPicker.DOM.scope.classList[action]('hidden')
165+
// remove the color-picker from the DOM
166+
if( showPicker ) {}
167+
showColorPicker(pickerElem)
168+
else
169+
hideColorPicker(pickerElem);
170170

171-
isTargetColorInput && observerCallback()
172-
},
171+
isTargetColorInput && observerCallback()
172+
},
173173

174-
onInput(c){
175-
colorInput.value = c;
176-
colorInput.style.setProperty('--color', c)
177-
},
174+
onInput(c){
175+
colorInput.value = c;
176+
colorInput.style.setProperty('--color', c)
177+
},
178178

179-
// onChange: console.log
180-
})
179+
// onChange: console.log
180+
})
181181

182-
cPicker.DOM.scope.setAttribute('positioned', true)
183-
document.body.appendChild(cPicker.DOM.scope)
182+
cPicker.DOM.scope.setAttribute('positioned', true)
183+
// document.body.appendChild(cPicker.DOM.scope)
184184

185-
resizeObserver.observe(document.body)
186-
intersectionObserver.observe(cPicker.DOM.scope)
187-
observerCallback()
185+
resizeObserver.observe(document.body)
186+
intersectionObserver.observe(cPicker.DOM.scope)
187+
observerCallback()
188188

189-
colorInput._colorPicker = cPicker
189+
// assign a custom property to color-input element
190+
// which points to the corresponding color-picker instance
191+
colorInput._colorPicker = cPicker
190192
})
191193

194+
function showColorPicker(pickerElem) {
195+
// if picker isn't already in the DOM:
196+
if( !document.body.contains(pickerElem) )
197+
document.body.appendChild(pickerElem); // inject to DOM
198+
}
199+
200+
function hideColorPicker(pickerElem) {
201+
pickerElem.remove();
202+
}
203+
192204
// setTimeout(() => {
193205
// document.querySelector('.myColor')._colorPicker.setColor( 'red' )
194206
// }, 2000)

0 commit comments

Comments
 (0)