Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions public/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@
"select_template": "Select template",
"rename": "Rename",
"color": "Color",
"color_gradient": "Gradient color",
"find_replace": "Find and replace",
"edit_list": "Edit list",
"timer": "Timer",
Expand Down Expand Up @@ -1382,6 +1383,16 @@
"cubic": "Cubic",
"sine": "Flow"
},
"color": {
"normal": "Normal",
"gradient": "Gradient",
"linear": "Linear",
"radial": "Radial",
"angle": "Angle",
"shape": "Shape",
"circle": "Circle",
"ellipse": "Ellipse"
},
"variables": {
"number": "Number",
"random_number": "Random number",
Expand Down
1 change: 0 additions & 1 deletion src/frontend/components/edit/tools/EditValues.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,6 @@
{value}
fontStyleValue={input.styleValue || ""}
disabled={typeof input.disabled === "string" ? item?.[input.disabled] || edits[section].find((a) => a.id === input.disabled)?.value : input.disabled}
enableNoColor={input.enableNoColor}
disableHold
on:click={(e) => valueChange(e, input)}
on:fontStyle={(e) => valueChange(e, { ...input, key: "font" })}
Expand Down
18 changes: 15 additions & 3 deletions src/frontend/components/edit/tools/ItemStyle.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@

// $: if (data) dataChanged()
function dataChanged() {
setBoxInputValue({ icon: "", edit: itemEditValues }, "default", "background-opacity", "hidden", !data["background-color"])
// gradient
const styles = getStyles(item?.style)
const isGradient = styles.background?.includes("gradient")
if (isGradient) data["background-color"] = styles.background

setBoxInputValue({ icon: "", edit: itemEditValues }, "default", "background-opacity", "hidden", isGradient || !data["background-color"])

data = stylePosToPercentage(data)
}
Expand Down Expand Up @@ -63,6 +68,13 @@
input.key = input.id
}

if (input.id === "style" && input.key === "background-color") {
// set "background" value instead of "background-color"
if (input.value.includes("gradient")) input.key = "background"
// reset "background" value
else updateStyle({ detail: { ...input, key: "background", value: "" } })
}

// background opacity
if (input.id === "background-opacity" || (input.value && input.key === "background-color")) {
input = setBackgroundColor(input, data)
Expand Down Expand Up @@ -147,7 +159,7 @@
id: "UPDATE",
oldData: { id: $activeEdit.id },
newData: { key: "items", subkey: "style", data: Object.values(values)[0], indexes: allItems },
location: { page: "edit", id: $activeEdit.type + "_items", override: true },
location: { page: "edit", id: $activeEdit.type + "_items", override: true }
})
return
}
Expand All @@ -157,7 +169,7 @@
history({
id: "setItems",
newData: { style: { key: "style", values: values[slide] } },
location: { page: "edit", show: $activeShow!, slide, items: slideItems[i], override: "slideitem_" + slide + "_items_" + slideItems[i].join(",") },
location: { page: "edit", show: $activeShow!, slide, items: slideItems[i], override: "slideitem_" + slide + "_items_" + slideItems[i].join(",") }
})
})
}
Expand Down
9 changes: 4 additions & 5 deletions src/frontend/components/edit/values/boxes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export type EditInput = {
valueIndex?: number
values?: any
popup?: string
enableNoColor?: boolean
slider?: boolean // include number slider
sliderValues?: any // custom number slider values
styleValue?: string // custom css styling
Expand Down Expand Up @@ -132,13 +131,13 @@ export const boxes: Box = {
},
},
// probably not needed as we have line and item background color
// { name: "background_color", id: "style", key: "background-color", input: "color", value: "rgb(0 0 0 / 0)", enableNoColor: true },
// { name: "background_color", id: "style", key: "background-color", input: "color", value: "rgb(0 0 0 / 0)", values: { enableNoColor: true } },
{ name: "no_wrap", id: "nowrap", input: "checkbox", value: false },
],
lines: [
{ name: "line_height", id: "style", key: "line-height", input: "number", value: 1.1, values: { max: 5, step: 0.1, decimals: 1, inputMultiplier: 10 }, extension: "em" },
{ name: "line_spacing", id: "specialStyle.lineGap", input: "number", value: 0, values: { max: 500 } },
{ name: "background_color", id: "specialStyle.lineBg", input: "color", value: "", enableNoColor: true },
{ name: "background_color", id: "specialStyle.lineBg", input: "color", value: "", values: { allowGradients: true, enableNoColor: true } },
{ name: "background_opacity", id: "specialStyle.opacity", input: "number", value: 1, values: { step: 0.1, decimals: 1, min: 0.1, max: 1, inputMultiplier: 10 } },
],
list: [
Expand Down Expand Up @@ -343,7 +342,7 @@ export const boxes: Box = {
style: [
{ name: "letter_spacing", id: "style", key: "letter-spacing", input: "number", value: 0, values: { max: 100, min: -1000 }, extension: "px" },
{ name: "line_height", id: "style", key: "line-height", input: "number", value: 1.1, values: { max: 10, step: 0.1, decimals: 1, inputMultiplier: 10 }, extension: "em" },
// { name: "background_color", id: "specialStyle.lineBg", input: "color", value: "", enableNoColor: true },
// { name: "background_color", id: "specialStyle.lineBg", input: "color", value: "", values: { enableNoColor: true } },
// { name: "background_opacity", id: "specialStyle.opacity", input: "number", value: 1, values: { step: 0.1, decimals: 1, max: 1, inputMultiplier: 10 } },
],

Expand Down Expand Up @@ -550,7 +549,7 @@ export const boxes: Box = {
icon: "visualizer",
edit: {
default: [
{ name: "color", id: "visualizer.color", input: "color", value: "rgb(0 0 0 / 0)", enableNoColor: true },
{ name: "color", id: "visualizer.color", input: "color", value: "rgb(0 0 0 / 0)", values: { enableNoColor: true } },
{ name: "padding", id: "visualizer.padding", input: "number", value: 0 },
],
},
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/components/edit/values/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const borderOptions = [

export const itemEdits: { [key: string]: EditInput[] } = {
default: [
{ name: "background_color", id: "style", key: "background-color", input: "color", value: "", enableNoColor: true },
{ name: "background_color", id: "style", key: "background-color", input: "color", value: "", values: { allowGradients: true, enableNoColor: true } },
{ name: "background_opacity", id: "background-opacity", input: "number", value: 0, values: { step: 0.1, decimals: 1, min: 0.1, max: 1, inputMultiplier: 10 } },
{ name: "opacity", id: "style", key: "opacity", input: "number", value: 1, values: { step: 0.1, decimals: 1, min: 0.1, max: 1, inputMultiplier: 10 } },
{ name: "padding", id: "style", key: "padding", input: "number", value: 0, extension: "px", values: { max: 300 } },
Expand Down
130 changes: 130 additions & 0 deletions src/frontend/components/helpers/color.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,34 @@ export const defaultColors = [
{ name: "Black", value: "#000000" },
]

export const defaultGradients = [
// Single
// { name: "White", value: "linear-gradient(120deg, #FFFFFF 0%, #DDDDDD 60%, #AAAAAA 100%)" },
{ name: "Silver", value: "linear-gradient(120deg, #DDDDDD 0%, #BBBBBB 60%, #999999 100%)" },
// { name: "Gray", value: "linear-gradient(120deg, #AAAAAA 0%, #888888 60%, #666666 100%)" },
{ name: "Red", value: "linear-gradient(120deg, #FF4136 0%, #C7002D 60%, #800020 100%)" },
{ name: "Orange", value: "linear-gradient(120deg, #FF851B 0%, #E06A00 60%, #A34700 100%)" },
{ name: "Yellow", value: "linear-gradient(120deg, #FFDC00 0%, #D4AF00 60%, #A18600 100%)" },
{ name: "Green", value: "linear-gradient(120deg, #2ECC40 0%, #1F9E2E 60%, #156F20 100%)" },
// { name: "Lime", value: "linear-gradient(120deg, #01FF70 0%, #00CC58 60%, #009944 100%)" },
// { name: "Olive", value: "linear-gradient(120deg, #3D9970 0%, #2E7354 60%, #204F39 100%)" },
{ name: "Teal", value: "linear-gradient(120deg, #39CCCC 0%, #2CA0A0 60%, #1E7575 100%)" },
{ name: "Aqua", value: "linear-gradient(120deg, #7FDBFF 0%, #52B8E6 60%, #2985B5 100%)" },
{ name: "Blue", value: "linear-gradient(120deg, #0074D9 0%, #0051A3 60%, #00346C 100%)" },
// { name: "Navy", value: "linear-gradient(120deg, #001f3f 0%, #00152C 60%, #000A19 100%)" },
{ name: "Purple", value: "linear-gradient(120deg, #B10DC9 0%, #8A0AA4 60%, #5F0578 100%)" },
// { name: "Fuchsia", value: "linear-gradient(120deg, #F012BE 0%, #C00999 60%, #900572 100%)" },
// { name: "Maroon", value: "linear-gradient(120deg, #85144b 0%, #5E0F35 60%, #3D0A23 100%)" },
{ name: "Black", value: "linear-gradient(120deg, #000000 0%, #222222 60%, #444444 100%)" },
// Multiple
{ name: "Pink to Purple", value: "linear-gradient(120deg,rgba(255,128,212, 1) 0%,rgba(193,47,106, 1) 62%,rgba(167,19,45, 1) 100%)" },
{ name: "Blue to Purple", value: "linear-gradient(120deg, #7FDBFF 0%, #c74076 62%, #b91533 100%)" },
{ name: "Orange to Purple", value: "linear-gradient(120deg, #FF851B 0%, #c74076 62%, #b91533 100%)" },
// Radial
{ name: "Purple Circle", value: "radial-gradient(circle, #A73537 0%, #AB087D 50%, #7C1DE8 100%)" },
{ name: "Blue Circle", value: "radial-gradient(circle, #39CCCC 0%, #0074D9 50%, #001f3f 100%)" },
]

export function hexToRgb(hex: string) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)

Expand Down Expand Up @@ -47,3 +75,105 @@ export function getContrast(hex: string) {
if (rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114 > 186) color = "#000000"
return color
}

// GRADIENT

// split gradient value
export function splitGradientValue(gradientStr: string) {
const result = {
type: "",
deg: 0,
shape: "circle",
colors: [] as { color: string; pos: number }[],
}

// remove whitespace and the trailing semicolon if present
gradientStr = gradientStr.trim().replace(/;$/, "")

// get type
const typeMatch = gradientStr.match(/^([a-zA-Z-]+)\(/)
if (!typeMatch) return result
result.type = typeMatch[1]

// const gradientRegex = /^(\w+-gradient)\(([^)]+)\)$/
// const match = gradientStr.match(gradientRegex)
// if (!match) return result
// result.type = match[1] // e.g. "linear-gradient"
// const inner = match[2] // everything inside the parentheses

// extract inside of parentheses
const openParenIndex = gradientStr.indexOf("(")
const content = gradientStr.slice(openParenIndex + 1, -1) // remove outer parentheses

// split components (angle + color stops)
const parts: string[] = []
let buffer = ""
let depth = 0

for (let char of content) {
if (char === "(") depth++
if (char === ")") depth--
if (char === "," && depth === 0) {
parts.push(buffer.trim())
buffer = ""
} else {
buffer += char
}
}
if (buffer) parts.push(buffer.trim())

// Linear-gradient: look for angle
if (result.type === "linear-gradient") {
const angleRegex = /^(\d+(?:\.\d+)?)(deg)?$/i
if (angleRegex.test(parts[0])) {
result.deg = parseFloat(parts[0])
parts.shift()
} else {
result.deg = 180 // default
}
}

// Radial-gradient: look for shape/size
if (result.type === "radial-gradient") {
const shapeKeywords = ["circle", "ellipse"]
if (shapeKeywords.includes(parts[0].toLowerCase())) {
result.shape = parts[0].toLowerCase()
parts.shift()
} else {
result.shape = "ellipse" // CSS default
}
}

// Normalize rgb(...) to rgba(...)
function normalizeColor(colorStr) {
if (/^rgb\(/i.test(colorStr)) {
const inside = colorStr.slice(4, -1).trim()
const values = inside.includes(",") ? inside.split(",") : inside.split(/\s+/)
if (values.length === 3) {
return `rgba(${values.join(",")}, 1)`
} else if (values.length === 4) {
return `rgba(${values.join(",")})`
}
}
return colorStr.trim()
}

// Parse color stops
for (const part of parts) {
const lastSpaceIndex = part.lastIndexOf(" ")
let color,
pos = 0

if (lastSpaceIndex !== -1 && /[\d.]+%$/.test(part.slice(lastSpaceIndex + 1))) {
color = part.slice(0, lastSpaceIndex).trim()
pos = parseFloat(part.slice(lastSpaceIndex + 1))
} else {
color = part.trim()
}

color = normalizeColor(color)
result.colors.push({ color, pos })
}

return result
}
Loading