babbcb1dc5
- New 'custom' ThemeId with dedicated ThemeCard + pencil edit button - CustomStyleModal: two-column editor for all node types (excl. groupRect/group) and edge types - Per node: border, background, icon color + opacity, default size (w/h) - Per edge: color + opacity, path style, animation - 'Apply to existing [Type]' per type, 'Apply All to Canvas' footer action - canvasStore: applyTypeNodeStyle, applyTypeEdgeStyle, applyAllCustomStyles actions - themeStore: customStyle state + setCustomStyle action - Backend: custom_style JSON column on canvas_state (ALTER TABLE migration), saved/loaded with canvas - App.tsx: custom_style included in save payload, restored on load (API + standalone) - Tests: +8 frontend (themeStore + canvasStore), +3 backend (canvas API)
44 lines
1.4 KiB
TypeScript
44 lines
1.4 KiB
TypeScript
/**
|
||
* Split a 6- or 8-digit hex color into its RGB hex and alpha (0–100).
|
||
* 6-digit input returns alpha 100.
|
||
* Invalid input returns { hex6: '#000000', alpha: 100 }.
|
||
*/
|
||
export function hexToRgba(hex: string): { hex6: string; alpha: number } {
|
||
const clean = hex.replace('#', '')
|
||
if (clean.length === 8) {
|
||
const alphaByte = parseInt(clean.slice(6, 8), 16)
|
||
return {
|
||
hex6: `#${clean.slice(0, 6)}`,
|
||
alpha: Math.round((alphaByte / 255) * 100),
|
||
}
|
||
}
|
||
if (clean.length === 6) {
|
||
return { hex6: `#${clean}`, alpha: 100 }
|
||
}
|
||
return { hex6: '#000000', alpha: 100 }
|
||
}
|
||
|
||
/**
|
||
* Combine a 6-digit hex color and an alpha (0–100) into an 8-digit hex.
|
||
*/
|
||
export function rgbaToHex8(hex6: string, alpha: number): string {
|
||
const clamped = Math.max(0, Math.min(100, alpha))
|
||
const alphaByte = Math.round((clamped / 100) * 255)
|
||
const alphaHex = alphaByte.toString(16).padStart(2, '0')
|
||
return `${hex6}${alphaHex}`
|
||
}
|
||
|
||
/**
|
||
* Combine a hex color and opacity (0–1) into a CSS rgba() string.
|
||
* Returns the plain hex when opacity is 1.
|
||
*/
|
||
export function applyOpacity(hex: string, opacity: number): string {
|
||
if (opacity >= 1) return hex
|
||
let h = hex.replace('#', '')
|
||
if (h.length === 3) h = h[0]+h[0]+h[1]+h[1]+h[2]+h[2]
|
||
const r = parseInt(h.slice(0, 2), 16)
|
||
const g = parseInt(h.slice(2, 4), 16)
|
||
const b = parseInt(h.slice(4, 6), 16)
|
||
return `rgba(${r}, ${g}, ${b}, ${Math.round(opacity * 100) / 100})`
|
||
}
|