Skip to content

Commit

Permalink
Added background options to CTA card
Browse files Browse the repository at this point in the history
Ref https://linear.app/ghost/issue/PLG-309/add-static-html-for-new-cta-card
- Added color swatches to the CTA card settings panel
- Replaced the hasBackground property with a no-background swatch
- Added a tooltip to the color picker in callout, signup, header and CTA cards
- Swapped white and grey swatches in the callout card
  • Loading branch information
sanne-san committed Jan 21, 2025
1 parent a7bb54e commit 5a4d692
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import PlusIcon from '../../assets/icons/plus.svg?react';
import React from 'react';
import {Tooltip} from './Tooltip';
import {usePreviousFocus} from '../../hooks/usePreviousFocus';

export function ColorOptionButtons({buttons = [], selectedName, onClick}) {
Expand Down Expand Up @@ -37,15 +38,16 @@ export function ColorButton({onClick, label, name, color, selectedName}) {
<li>
<button
aria-label={label}
className={`flex size-[2.6rem] cursor-pointer items-center justify-center rounded-full border-2 ${isActive ? 'border-green' : 'border-transparent'}`}
className={`group relative flex size-[2.6rem] cursor-pointer items-center justify-center rounded-full border-2 ${isActive ? 'border-green' : 'border-transparent'}`}
data-test-id={`color-picker-${name}`}
type="button"
onClick={handleClick}
onMouseDown={handleMousedown}
>
<span
className={`${color} size-5 rounded-full border border-black/5`}
className={`${color} size-5 rounded-full border`}
></span>
<Tooltip label={label} />
</button>
</li>
);
Expand Down
7 changes: 5 additions & 2 deletions packages/koenig-lexical/src/components/ui/ColorPicker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, {Fragment, useCallback, useEffect, useRef} 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}) {
Expand Down Expand Up @@ -119,7 +120,7 @@ function ColorSwatch({hex, accent, transparent, title, isSelected, onSelect}) {
<button
ref={ref}
className={clsx(
`relative flex size-5 shrink-0 items-center rounded-full border border-grey-200 dark:border-grey-800`,
`group relative flex size-5 shrink-0 items-center rounded-full border border-grey-250 dark:border-grey-800`,
isSelected && 'outline outline-2 outline-green'
)}
style={{backgroundColor}}
Expand All @@ -128,6 +129,7 @@ function ColorSwatch({hex, accent, transparent, title, isSelected, onSelect}) {
onClick={onSelectHandler}
>
{transparent && <div className="absolute left-0 top-0 z-10 w-[136%] origin-left rotate-45 border-b border-b-red" />}
<Tooltip label={title} />
</button>
);
}
Expand All @@ -154,13 +156,14 @@ export function ColorIndicator({value, swatches, onSwatchChange, onTogglePicker,
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="relative size-6 rounded-full border border-grey-200 dark:border-grey-800" type="button" onClick={onTogglePicker}>
<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>
)}
<Tooltip label='Pick color' />
</button>
</div>
);
Expand Down
32 changes: 16 additions & 16 deletions packages/koenig-lexical/src/components/ui/cards/CalloutCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import {ColorOptionSetting, SettingsPanel, ToggleSetting} from '../SettingsPanel
import {ReadOnlyOverlay} from '../ReadOnlyOverlay';

export const CALLOUT_COLORS = {
grey: 'bg-grey/10 border-transparent',
white: 'bg-transparent border-grey/30',
grey: 'bg-grey/10 border-transparent',
blue: 'bg-blue/10 border-transparent',
green: 'bg-green/10 border-transparent',
yellow: 'bg-yellow/10 border-transparent',
Expand All @@ -22,8 +22,8 @@ const TEXT_BLACK = 'text-black dark:text-grey-300 caret-black dark:caret-grey-30
const TEXT_WHITE = 'text-white caret-white';

export const CALLOUT_TEXT_COLORS = {
grey: TEXT_BLACK,
white: TEXT_BLACK,
grey: TEXT_BLACK,
blue: TEXT_BLACK,
green: TEXT_BLACK,
yellow: TEXT_BLACK,
Expand All @@ -35,50 +35,50 @@ export const CALLOUT_TEXT_COLORS = {
};

export const calloutColorPicker = [
{
label: 'Grey',
name: 'grey',
color: 'bg-grey/10 dark:border-white/10'
},
{
label: 'White',
name: 'white',
color: 'bg-transparent dark:border-white/10'
color: 'bg-transparent border-black/15 dark:border-white/10'
},
{
label: 'Grey',
name: 'grey',
color: 'bg-grey/15 border-black/[0.08] dark:border-white/10'
},
{
label: 'Blue',
name: 'blue',
color: 'bg-blue/10 dark:border-white/10'
color: 'bg-blue/15 border-black/[0.08] dark:border-white/10'
},
{
label: 'Green',
name: 'green',
color: 'bg-green/10 dark:border-white/10'
color: 'bg-green/15 border-black/[0.08] dark:border-white/10'
},
{
label: 'Yellow',
name: 'yellow',
color: 'bg-yellow/10 dark:border-white/10'
color: 'bg-yellow/15 border-black/[0.08] dark:border-white/10'
},
{
label: 'Red',
name: 'red',
color: 'bg-red/10 dark:border-white/10'
color: 'bg-red/15 border-black/[0.08] dark:border-white/10'
},
{
label: 'Pink',
name: 'pink',
color: 'bg-pink/10 dark:border-white/10'
color: 'bg-pink/15 border-black/[0.08] dark:border-white/10'
},
{
label: 'Purple',
name: 'purple',
color: 'bg-purple/10 dark:border-white/10'
color: 'bg-purple/15 border-black/[0.08] dark:border-white/10'
},
{
label: 'Accent',
name: 'accent',
color: 'bg-accent'
color: 'bg-accent border-black/[0.08] dark:border-white/10'
}
];

Expand Down Expand Up @@ -172,7 +172,7 @@ export function CalloutCard({
}

CalloutCard.propTypes = {
color: PropTypes.oneOf(['grey', 'white', 'blue', 'green', 'yellow', 'red', 'pink', 'purple', 'accent']),
color: PropTypes.oneOf(['white', 'grey', 'blue', 'green', 'yellow', 'red', 'pink', 'purple', 'accent']),
text: PropTypes.string,
hasEmoji: PropTypes.bool,
placeholder: PropTypes.string,
Expand Down
86 changes: 69 additions & 17 deletions packages/koenig-lexical/src/components/ui/cards/CtaCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,63 @@ import PropTypes from 'prop-types';
import React from 'react';
import ReplacementStringsPlugin from '../../../plugins/ReplacementStringsPlugin';
import {Button} from '../Button';
import {ButtonGroupSetting, InputSetting, InputUrlSetting, SettingsPanel, ToggleSetting} from '../SettingsPanel';
import {ButtonGroupSetting, ColorOptionSetting, InputSetting, InputUrlSetting, SettingsPanel, ToggleSetting} from '../SettingsPanel';
import {ReadOnlyOverlay} from '../ReadOnlyOverlay';

export const CALLOUT_COLORS = {
none: 'bg-transparent border-transparent',
grey: 'bg-grey/10 border-transparent',
white: 'bg-transparent border-grey-300 dark:border-grey-800',
blue: 'bg-blue/10 border-transparent',
green: 'bg-green/10 border-transparent',
yellow: 'bg-yellow/10 border-transparent',
red: 'bg-red/10 border-transparent',
pink: 'bg-pink/10 border-transparent',
purple: 'bg-purple/10 border-transparent'
};

export const calloutColorPicker = [
{
label: 'None',
name: 'none',
color: 'bg-transparent border-black/15 relative after:absolute after:left-1/2 after:top-1/2 after:h-[1px] after:w-[18px] after:-translate-x-1/2 after:-translate-y-1/2 after:-rotate-45 after:bg-red-500'
},
{
label: 'White',
name: 'white',
color: 'bg-transparent border-black/15 dark:border-white/10'
},
{
label: 'Grey',
name: 'grey',
color: 'bg-grey/15 border-black/[.08] dark:border-white/10'
},
{
label: 'Blue',
name: 'blue',
color: 'bg-blue/15 border-black/[.08] dark:border-white/10'
},
{
label: 'Green',
name: 'green',
color: 'bg-green/15 border-black/[.08] dark:border-white/10'
},
{
label: 'Yellow',
name: 'yellow',
color: 'bg-yellow/15 border-black/[.08] dark:border-white/10'
},
{
label: 'Red',
name: 'red',
color: 'bg-red/15 border-black/[.08] dark:border-white/10'
}
];

export function CtaCard({
buttonText,
buttonUrl,
hasBackground,
color,
hasImage,
hasSponsorLabel,
htmlEditor,
Expand All @@ -23,10 +73,10 @@ export function CtaCard({
updateButtonText,
updateButtonUrl,
updateShowButton,
updateHasBackground,
updateHasSponsorLabel,
updateHasImage,
updateLayout
updateLayout,
handleColorChange
}) {
const layoutOptions = [
{
Expand All @@ -45,15 +95,15 @@ export function CtaCard({

return (
<>
<div className={`w-full ${hasBackground ? 'rounded-lg bg-grey-100 dark:bg-grey-900' : ''} ${(isEditing || isSelected) && !hasBackground ? 'pb-3' : ''}`}>
<div className={`w-full rounded-lg border ${CALLOUT_COLORS[color]} ${(isEditing || isSelected) && color === 'none' ? 'pb-3' : ''}`}>
{/* Sponsor label */}
{hasSponsorLabel && (
<div className={`not-kg-prose py-3 ${hasBackground ? 'mx-5' : ''}`}>
<div className={`not-kg-prose py-3 ${color === 'none' ? '' : 'mx-5'}`}>
<p className="font-sans text-2xs font-semibold uppercase leading-8 tracking-normal text-grey dark:text-grey-800">Sponsored</p>
</div>
)}

<div className={`flex ${layout === 'immersive' ? 'flex-col' : 'flex-row'} gap-5 py-5 ${hasSponsorLabel || !hasBackground ? 'border-t border-grey-300 dark:border-grey-800' : ''} ${hasBackground ? 'mx-5' : 'border-b border-grey-300 dark:border-grey-800'}`}>
<div className={`flex ${layout === 'immersive' ? 'flex-col' : 'flex-row'} gap-5 py-5 ${color === 'none' || hasSponsorLabel ? 'border-t border-grey-300 dark:border-grey-800' : ''} ${color === 'none' ? 'border-b border-grey-300 dark:border-grey-800' : 'mx-5'}`}>
{hasImage && (
<div className={`block ${layout === 'immersive' ? 'w-full' : 'w-16 shrink-0'}`}>
<img alt="Placeholder" className={`${layout === 'immersive' ? 'h-auto w-full' : 'aspect-square w-16 object-cover'} rounded-md`} src="https://images.unsplash.com/photo-1511556532299-8f662fc26c06?q=80&w=4431&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
Expand Down Expand Up @@ -103,11 +153,13 @@ export function CtaCard({
selectedName={layout}
onClick={updateLayout}
/>
{/* Background setting */}
<ToggleSetting
isChecked={hasBackground}
{/* Color picker */}
<ColorOptionSetting
buttons={calloutColorPicker}
dataTestId='callout-color-picker'
label='Background'
onChange={updateHasBackground}
selectedName={color}
onClick={handleColorChange}
/>
{/* Sponsor label setting */}
<ToggleSetting
Expand Down Expand Up @@ -154,7 +206,7 @@ export function CtaCard({
CtaCard.propTypes = {
buttonText: PropTypes.string,
buttonUrl: PropTypes.string,
hasBackground: PropTypes.bool,
color: PropTypes.oneOf(['none', 'grey', 'white', 'blue', 'green', 'yellow', 'red']),
hasImage: PropTypes.bool,
hasSponsorLabel: PropTypes.bool,
isEditing: PropTypes.bool,
Expand All @@ -165,26 +217,26 @@ CtaCard.propTypes = {
htmlEditorInitialState: PropTypes.object,
updateButtonText: PropTypes.func,
updateButtonUrl: PropTypes.func,
updateHasBackground: PropTypes.func,
updateHasSponsorLabel: PropTypes.func,
updateHasImage: PropTypes.func,
updateShowButton: PropTypes.func,
updateLayout: PropTypes.func
updateLayout: PropTypes.func,
handleColorChange: PropTypes.func
};

CtaCard.defaultProps = {
buttonText: '',
buttonUrl: '',
hasBackground: false,
color: 'none',
hasImage: false,
hasSponsorLabel: false,
isEditing: false,
isSelected: false,
layout: 'immersive',
showButton: false,
updateHasBackground: () => {},
updateHasSponsorLabel: () => {},
updateHasImage: () => {},
updateShowButton: () => {},
updateLayout: () => {}
updateLayout: () => {},
handleColorChange: () => {}
};
Original file line number Diff line number Diff line change
Expand Up @@ -64,26 +64,26 @@ const Template = ({display, value, ...args}) => {
<div className="mx-auto my-8 min-w-[initial] max-w-[740px]">
<CardWrapper
IndicatorIcon={EmailIndicatorIcon}
{...(!args.hasBackground && {wrapperStyle: 'wide'})}
{...(args.color === 'none' && {wrapperStyle: 'wide'})}
{...display}
{...args}
>
<CtaCard {...display} {...args} htmlEditor={htmlEditor} />
</CardWrapper>
</div>
</div>
<div className="kg-prose dark bg-black px-4 py-8">
{/* <div className="kg-prose dark bg-black px-4 py-8">
<div className="mx-auto my-8 min-w-[initial] max-w-[740px]">
<CardWrapper
IndicatorIcon={EmailIndicatorIcon}
{...(!args.hasBackground && {wrapperStyle: 'wide'})}
{...(args.color === 'none' && {wrapperStyle: 'wide'})}
{...display}
{...args}
>
<CtaCard {...display} {...args} htmlEditor={htmlEditor} />
</CardWrapper>
</div>
</div>
</div> */}
</div>
);
};
Expand All @@ -93,9 +93,9 @@ Empty.args = {
display: 'Editing',
value: '',
showButton: false,
hasBackground: false,
hasImage: false,
hasSponsorLabel: false,
color: 'green',
layout: 'immersive',
buttonText: '',
buttonUrl: '',
Expand All @@ -109,7 +109,7 @@ Populated.args = {
showButton: true,
hasImage: true,
hasSponsorLabel: true,
hasBackground: false,
color: 'green',
layout: 'immersive',
buttonText: 'Upgrade',
buttonUrl: 'https://ghost.org/',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import {FastAverageColor} from 'fast-average-color';
import {IconButton} from '../../../IconButton';
import {MediaUploader} from '../../../MediaUploader';
import {ReadOnlyOverlay} from '../../../ReadOnlyOverlay';
import {Tooltip} from '../../../Tooltip';
import {getAccentColor} from '../../../../../utils/getAccentColor';
import {isEditorEmpty} from '../../../../../utils/isEditorEmpty';

// Header Card Version 2
export function HeaderCard({alignment,
buttonEnabled,
Expand Down Expand Up @@ -407,7 +407,7 @@ export function HeaderCard({alignment,
customContent: (
<button
className={clsx(
`relative flex size-6 shrink-0 items-center justify-center rounded-full border border-grey-300 bg-grey-100 text-black`,
`group relative flex size-6 shrink-0 items-center justify-center rounded-full border border-grey-300 bg-grey-100 text-black`,
showBackgroundImage && 'outline outline-2 outline-green'
)}
data-testid="header-background-image-toggle"
Expand All @@ -420,6 +420,7 @@ export function HeaderCard({alignment,
}}
>
<ImgBgIcon className="size-[1.4rem]" />
<Tooltip label='Image' />
</button>
)
}),
Expand Down
Loading

0 comments on commit 5a4d692

Please sign in to comment.