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

Organised CTA card settings panel #1449

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ export class CallToActionNode extends generateDecoratorNode({
properties: [
{name: 'layout', default: 'minimal'},
{name: 'textValue', default: '', wordCount: true},
{name: 'showButton', default: false},
{name: 'buttonText', default: ''},
{name: 'showButton', default: true},
{name: 'buttonText', default: 'Learn more'},
{name: 'buttonUrl', default: ''},
{name: 'buttonColor', default: ''},
{name: 'buttonColor', default: 'black'},
{name: 'buttonTextColor', default: ''},
{name: 'hasSponsorLabel', default: true},
{name: 'sponsorLabel', default: '<p><span style="white-space: pre-wrap;">SPONSORED</span></p>'},
Expand Down
4 changes: 2 additions & 2 deletions packages/koenig-lexical/src/components/ui/ButtonGroup.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {usePreviousFocus} from '../../hooks/usePreviousFocus';
export function ButtonGroup({buttons = [], selectedName, onClick}) {
return (
<div className="flex">
<ul className="flex items-center justify-evenly gap-[.6rem] rounded-lg font-sans text-md font-normal text-white">
<ul className="flex items-center justify-evenly rounded-lg bg-grey-100 font-sans text-md font-normal text-white">
{buttons.map(({label, name, Icon, dataTestId}) => (
<IconButton
key={`${name}-${label}`}
Expand All @@ -33,7 +33,7 @@ export function IconButton({dataTestId, onClick, label, name, selectedName, Icon
<li className="mb-0">
<button
aria-label={label}
className={`group relative flex h-7 w-8 cursor-pointer items-center justify-center rounded-lg hover:bg-grey-150 dark:hover:bg-grey-900 ${isActive ? 'bg-grey-150 text-green-600 dark:bg-grey-900 dark:text-white' : 'text-black dark:text-white' } ${Icon ? '' : 'text-[1.3rem] font-bold'}`}
className={`group relative flex h-7 w-8 cursor-pointer items-center justify-center rounded-lg text-black dark:text-white dark:hover:bg-grey-900 ${isActive ? 'border border-grey-300 bg-white shadow-xs dark:bg-grey-900' : '' } ${Icon ? '' : 'text-[1.3rem] font-bold'}`}
data-testid={dataTestId}
type="button"
onClick={handleClick}
Expand Down
75 changes: 53 additions & 22 deletions packages/koenig-lexical/src/components/ui/ColorOptionButtons.jsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,62 @@
import PlusIcon from '../../assets/icons/plus.svg?react';
import React from 'react';
import React, {useState} from 'react';
import {Tooltip} from './Tooltip';
import {usePreviousFocus} from '../../hooks/usePreviousFocus';

export function ColorOptionButtons({buttons = [], selectedName, onClick}) {
const [isOpen, setIsOpen] = useState(false);
const selectedButton = buttons.find(button => button.name === selectedName);

return (
<div className="flex">
<ul className="flex w-full items-center justify-between rounded-md font-sans text-md font-normal text-white">
{buttons.map(({label, name, color}) => (
name !== 'image' ?
<ColorButton
key={`${name}-${label}`}
color={color}
label={label}
name={name}
selectedName={selectedName}
onClick={onClick}
/>
:
<li key='background-image' className={`flex size-[3rem] cursor-pointer items-center justify-center rounded-full border-2 ${selectedName === name ? 'border-green' : 'border-transparent'}`} data-testid="background-image-color-button" type="button" onClick={() => onClick(name)}>
<span className="border-1 flex size-6 items-center justify-center rounded-full border border-black/5">
<PlusIcon className="size-3 stroke-grey-700 stroke-2 dark:stroke-grey-500 dark:group-hover:stroke-grey-100" />
</span>
</li>
<div className="relative">
<button
className={`relative size-6 cursor-pointer rounded-full ${selectedName ? 'p-[2px]' : 'border border-grey-200 dark:border-grey-800'}`}
type="button"
onClick={() => setIsOpen(!isOpen)}
>
{selectedName && (
<div className="absolute inset-0 rounded-full bg-clip-content p-[3px]" style={{
background: 'conic-gradient(hsl(360,100%,50%),hsl(315,100%,50%),hsl(270,100%,50%),hsl(225,100%,50%),hsl(180,100%,50%),hsl(135,100%,50%),hsl(90,100%,50%),hsl(45,100%,50%),hsl(0,100%,50%))',
WebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
WebkitMaskComposite: 'xor',
mask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
maskComposite: 'exclude'
}} />
)}
<span
className={`${selectedButton?.color || ''} block size-full rounded-full border-2 border-white`}
></span>
</button>

))}
</ul>
{/* Color options popover */}
{isOpen && (
<div className="absolute -right-3 bottom-full z-10 mb-2 rounded-lg bg-white px-3 py-2 shadow">
<div className="flex">
<ul className="flex w-full items-center justify-between rounded-md font-sans text-md font-normal text-white">
{buttons.map(({label, name, color}) => (
name !== 'image' ?
<ColorButton
key={`${name}-${label}`}
color={color}
label={label}
name={name}
selectedName={selectedName}
onClick={(name) => {
onClick(name);
setIsOpen(false);
}}
/>
:
<li key='background-image' className={`mb-0 flex size-[3rem] cursor-pointer items-center justify-center rounded-full border-2 ${selectedName === name ? 'border-green' : 'border-transparent'}`} data-testid="background-image-color-button" type="button" onClick={() => onClick(name)}>
<span className="border-1 flex size-6 items-center justify-center rounded-full border border-black/5">
<PlusIcon className="size-3 stroke-grey-700 stroke-2 dark:stroke-grey-500 dark:group-hover:stroke-grey-100" />
</span>
</li>
))}
</ul>
</div>
</div>
)}
</div>
);
}
Expand All @@ -35,7 +66,7 @@ export function ColorButton({onClick, label, name, color, selectedName}) {

const {handleMousedown, handleClick} = usePreviousFocus(onClick, name);
return (
<li>
<li className="mb-0">
<button
aria-label={label}
className={`group relative flex size-6 cursor-pointer items-center justify-center rounded-full border-2 ${isActive ? 'border-green' : 'border-transparent'}`}
Expand Down
142 changes: 125 additions & 17 deletions packages/koenig-lexical/src/components/ui/ColorPicker.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import EyedropperIcon from '../../assets/icons/kg-eyedropper.svg?react';
import React, {Fragment, useCallback, useEffect, useRef} from 'react';
import React, {Fragment, useCallback, useEffect, useRef, useState} from 'react';
import clsx from 'clsx';
import {Button} from './Button';
import {HexColorInput, HexColorPicker} from 'react-colorful';
import {Tooltip} from './Tooltip';
import {getAccentColor} from '../../utils/getAccentColor';

export function ColorPicker({value, eyedropper, hasTransparentOption, onChange}) {
export function ColorPicker({value, eyedropper, hasTransparentOption, onChange, children}) {
// HexColorInput doesn't support adding a ref on the input itself
const inputWrapperRef = useRef(null);

Expand Down Expand Up @@ -76,7 +76,7 @@ export function ColorPicker({value, eyedropper, hasTransparentOption, onChange})
}, []);

return (
<div className="mt-2" onMouseDown={stopPropagation} onTouchStart={stopPropagation}>
<div onMouseDown={stopPropagation} onTouchStart={stopPropagation}>
<HexColorPicker color={hexValue || '#ffffff'} onChange={onChange} onMouseDown={startUsingColorPicker} onTouchStart={startUsingColorPicker} />
<div className="mt-3 flex gap-2">
<div ref={inputWrapperRef} className={`relative flex w-full items-center rounded-lg border border-grey-100 bg-grey-100 px-3 py-1.5 font-sans text-sm font-normal text-grey-900 transition-colors placeholder:text-grey-500 focus-within:border-green focus-within:bg-white focus-within:shadow-[0_0_0_2px_rgba(48,207,67,.25)] focus-within:outline-none dark:border-transparent dark:bg-grey-900 dark:text-white dark:selection:bg-grey-800 dark:placeholder:text-grey-700 dark:focus-within:border-green dark:hover:bg-grey-925 dark:focus:bg-grey-925`} onClick={focusHexInputOnClick}>
Expand All @@ -94,6 +94,7 @@ export function ColorPicker({value, eyedropper, hasTransparentOption, onChange})
</div>

{hasTransparentOption && <Button color='grey' value='Clear' onClick={() => onChange('transparent')} />}
{children}
</div>
</div>
);
Expand Down Expand Up @@ -134,9 +135,28 @@ function ColorSwatch({hex, accent, transparent, title, isSelected, onSelect}) {
);
}

export function ColorIndicator({value, swatches, onSwatchChange, onTogglePicker, isExpanded}) {
export function ColorIndicator({value, swatches, onSwatchChange, onTogglePicker, onChange, isExpanded, eyedropper, hasTransparentOption, children}) {
const [isOpen, setIsOpen] = useState(false);
const [showColorPicker, setShowColorPicker] = useState(false);
const [showChildren, setShowChildren] = useState(false);
const popoverRef = useRef(null);

const stopPropagation = useCallback((e) => {
e.stopPropagation();
e.preventDefault();
}, []);

useEffect(() => {
if (isExpanded) {
setIsOpen(true);
setShowChildren(true);
setShowColorPicker(false);
}
}, [isExpanded]);

let backgroundColor = value;
let selectedSwatch = swatches.find(swatch => swatch.hex === value)?.title;

if (value === 'accent') {
backgroundColor = getAccentColor();
selectedSwatch = swatches.find(swatch => swatch.accent)?.title;
Expand All @@ -149,22 +169,110 @@ export function ColorIndicator({value, swatches, onSwatchChange, onTogglePicker,
selectedSwatch = null;
}

const handleColorPickerChange = (newValue) => {
onChange(newValue);
// Don't close the popover when using the color picker
};

return (
<div className='flex gap-1'>
<div className={`flex items-center gap-1`}>
{swatches.map(({customContent, ...swatch}) => (
customContent ? <Fragment key={swatch.title}>{customContent}</Fragment> : <ColorSwatch key={swatch.title} isSelected={selectedSwatch === swatch.title} onSelect={onSwatchChange} {...swatch} />
))}
</div>
<button aria-label="Pick color" className="group relative size-6 rounded-full border border-grey-200 dark:border-grey-800" type="button" onClick={onTogglePicker}>
<div className='absolute inset-0 rounded-full bg-[conic-gradient(hsl(360,100%,50%),hsl(315,100%,50%),hsl(270,100%,50%),hsl(225,100%,50%),hsl(180,100%,50%),hsl(135,100%,50%),hsl(90,100%,50%),hsl(45,100%,50%),hsl(0,100%,50%))]' />
{value && !selectedSwatch && (
<div className="absolute inset-[3px] overflow-hidden rounded-full border border-white dark:border-grey-950" style={{backgroundColor}}>
{value === 'transparent' && <div className="absolute left-[3px] top-[3px] z-10 w-[136%] origin-left rotate-45 border-b border-b-red" />}
</div>
<div className="relative">
<button
className={`relative size-6 cursor-pointer rounded-full ${value ? 'p-[2px]' : 'border border-grey-200 dark:border-grey-800'}`}
type="button"
onClick={() => {
setIsOpen(!isOpen);
setShowColorPicker(false);
setShowChildren(false);
}}
>
{value && (
<div className="absolute inset-0 rounded-full bg-clip-content p-[3px]" style={{
background: 'conic-gradient(hsl(360,100%,50%),hsl(315,100%,50%),hsl(270,100%,50%),hsl(225,100%,50%),hsl(180,100%,50%),hsl(135,100%,50%),hsl(90,100%,50%),hsl(45,100%,50%),hsl(0,100%,50%))',
WebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
WebkitMaskComposite: 'xor',
mask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
maskComposite: 'exclude'
}} />
)}
<Tooltip label='Pick color' />
<span
className="block size-full rounded-full border-2 border-white"
style={{backgroundColor}}
>
{value === 'transparent' && <div className="absolute left-[3px] top-[3px] z-10 w-[136%] origin-left rotate-45 border-b border-b-red" />}
</span>
</button>

{isOpen && (
<div
ref={popoverRef}
className={clsx(
'absolute -right-3 bottom-full z-10 mb-2 flex flex-col gap-3 rounded-lg bg-white p-3 shadow transition-[width] duration-200 ease-in-out dark:bg-grey-950',
(showColorPicker || showChildren) && 'min-w-[296px]'
)}
onClick={stopPropagation}
onMouseDown={stopPropagation}
onTouchStart={stopPropagation}
>
{showColorPicker && (
<ColorPicker
eyedropper={eyedropper}
hasTransparentOption={hasTransparentOption}
value={value}
onChange={handleColorPickerChange}
/>
)}
{showChildren && children}
<div className="flex justify-end gap-1">
<div className={`flex items-center gap-1`}>
{swatches.map(({customContent, ...swatch}) => (
customContent ?
<Fragment key={swatch.title}>{customContent}</Fragment> :
<ColorSwatch
key={swatch.title}
isSelected={selectedSwatch === swatch.title}
onSelect={(value) => {
onSwatchChange(value);
setShowColorPicker(false);
setIsOpen(false);
}}
{...swatch}
/>
))}
</div>
<button
aria-label="Pick color"
className={`group relative size-6 rounded-full ${!selectedSwatch ? 'p-[2px]' : 'border border-grey-200 dark:border-grey-800'}`}
type="button"
onClick={() => {
setShowColorPicker(!showColorPicker);
setShowChildren(false);
onTogglePicker();
}}
>
{!selectedSwatch ? (
<>
<div className="absolute inset-0 rounded-full bg-clip-content p-[3px]" style={{
background: 'conic-gradient(hsl(360,100%,50%),hsl(315,100%,50%),hsl(270,100%,50%),hsl(225,100%,50%),hsl(180,100%,50%),hsl(135,100%,50%),hsl(90,100%,50%),hsl(45,100%,50%),hsl(0,100%,50%))',
WebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
WebkitMaskComposite: 'xor',
mask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
maskComposite: 'exclude'
}} />
<span
className="block size-full rounded-full border-2 border-white"
style={{backgroundColor: value}}
>
{value === 'transparent' && <div className="absolute left-[3px] top-[3px] z-10 w-[136%] origin-left rotate-45 border-b border-b-red" />}
</span>
</>
) : (
<div className='absolute inset-0 rounded-full bg-[conic-gradient(hsl(360,100%,50%),hsl(315,100%,50%),hsl(270,100%,50%),hsl(225,100%,50%),hsl(180,100%,50%),hsl(135,100%,50%),hsl(90,100%,50%),hsl(45,100%,50%),hsl(0,100%,50%))]' />
)}
<Tooltip label='Pick color' />
</button>
</div>
</div>
)}
</div>
);
}
Loading
Loading