mirror of
https://github.com/documenso/documenso.git
synced 2026-07-14 06:47:10 +10:00
0b86ece1d5
Platform-plan organisations and teams can now customise non-embed signing pages with six brand colour tokens, a border-radius, and a free-text custom CSS block (up to 256 KB). - Stored on OrganisationGlobalSettings / TeamGlobalSettings; teams inherit from the org via brandingEnabled === null. - CSS is sanitised on save (PostCSS) so we can inline it at SSR with no per-render parsing. - Rendered via a nonce'd <style> scoped under .documenso-branded, using native CSS nesting so user selectors don't need scoping. - Gated on the existing embedSigningWhiteLabel claim (or self-hosted) — reuses the embed white-label decision.
90 lines
2.3 KiB
TypeScript
90 lines
2.3 KiB
TypeScript
import type { HTMLAttributes } from 'react';
|
|
import { useEffect, useState } from 'react';
|
|
|
|
import { HexColorInput, HexColorPicker, setNonce } from 'react-colorful';
|
|
|
|
import { cn } from '../lib/utils';
|
|
import { Popover, PopoverContent, PopoverTrigger } from './popover';
|
|
|
|
export type ColorPickerProps = {
|
|
disabled?: boolean;
|
|
value: string;
|
|
defaultValue?: string;
|
|
onChange: (color: string) => void;
|
|
nonce?: string;
|
|
} & HTMLAttributes<HTMLDivElement>;
|
|
|
|
export const ColorPicker = ({
|
|
className,
|
|
disabled = false,
|
|
value,
|
|
defaultValue = '#000000',
|
|
onChange,
|
|
nonce,
|
|
...props
|
|
}: ColorPickerProps) => {
|
|
const [color, setColor] = useState(value || defaultValue);
|
|
const [inputColor, setInputColor] = useState(value || defaultValue);
|
|
|
|
const onColorChange = (newColor: string) => {
|
|
setColor(newColor);
|
|
setInputColor(newColor);
|
|
onChange(newColor);
|
|
};
|
|
|
|
const onInputChange = (newColor: string) => {
|
|
setInputColor(newColor);
|
|
};
|
|
|
|
const onInputBlur = () => {
|
|
setColor(inputColor);
|
|
onChange(inputColor);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (nonce) {
|
|
setNonce(nonce);
|
|
}
|
|
}, [nonce]);
|
|
|
|
return (
|
|
<Popover>
|
|
<PopoverTrigger>
|
|
<button
|
|
type="button"
|
|
disabled={disabled}
|
|
className="h-12 w-12 rounded-md border bg-background p-1 disabled:pointer-events-none disabled:opacity-50"
|
|
>
|
|
<div className="h-full w-full rounded-sm" style={{ backgroundColor: color }} />
|
|
</button>
|
|
</PopoverTrigger>
|
|
|
|
<PopoverContent className="w-auto">
|
|
<HexColorPicker
|
|
className={cn(className, 'w-full aria-disabled:pointer-events-none aria-disabled:opacity-50')}
|
|
color={color}
|
|
onChange={onColorChange}
|
|
aria-disabled={disabled}
|
|
nonce={nonce}
|
|
{...props}
|
|
/>
|
|
|
|
<HexColorInput
|
|
className="mt-4 h-10 rounded-md border bg-transparent px-3 py-2 text-sm disabled:pointer-events-none disabled:opacity-50"
|
|
color={inputColor}
|
|
onChange={onInputChange}
|
|
onBlur={onInputBlur}
|
|
onKeyDown={(e) => {
|
|
if (e.key === 'Enter') {
|
|
e.preventDefault();
|
|
onInputBlur();
|
|
}
|
|
}}
|
|
disabled={disabled}
|
|
nonce={nonce}
|
|
/>
|
|
</PopoverContent>
|
|
</Popover>
|
|
);
|
|
};
|