feat: add gradient entity modal and fix color picker clipping
Add full gradient editor modal with name, description, visual stop editor, tags, and dirty checking. Gradient editor now supports ID prefix to avoid DOM conflicts between CSS editor and standalone modal. Fix color picker popover clipped by template-card overflow:hidden. Fix gradient canvas not sizing correctly in standalone modal.
This commit is contained in:
@@ -54,6 +54,12 @@ let _gradientStops: GradientStop[] = [];
|
||||
let _gradientSelectedIdx: number = -1;
|
||||
let _gradientDragging: GradientDragState | null = null;
|
||||
let _gradientOnChange: (() => void) | null = null;
|
||||
let _idPrefix: string = '';
|
||||
|
||||
/** Set an ID prefix for DOM elements (e.g. 'ge-' to find 'ge-gradient-canvas'). */
|
||||
export function gradientSetIdPrefix(prefix: string): void { _idPrefix = prefix; }
|
||||
|
||||
function _el(id: string): HTMLElement | null { return document.getElementById(_idPrefix + id); }
|
||||
|
||||
/** Set a callback that fires whenever stops change. */
|
||||
export function gradientSetOnChange(fn: (() => void) | null): void { _gradientOnChange = fn; }
|
||||
@@ -215,7 +221,7 @@ export function gradientRenderAll(): void {
|
||||
}
|
||||
|
||||
function _gradientRenderCanvas(): void {
|
||||
const canvas = document.getElementById('gradient-canvas') as HTMLCanvasElement | null;
|
||||
const canvas = _el('gradient-canvas') as HTMLCanvasElement | null;
|
||||
if (!canvas) return;
|
||||
|
||||
// Sync canvas pixel width to its CSS display width
|
||||
@@ -241,7 +247,7 @@ function _gradientRenderCanvas(): void {
|
||||
}
|
||||
|
||||
function _gradientRenderMarkers(): void {
|
||||
const track = document.getElementById('gradient-markers-track');
|
||||
const track = _el('gradient-markers-track');
|
||||
if (!track) return;
|
||||
track.innerHTML = '';
|
||||
|
||||
@@ -276,7 +282,7 @@ function _gradientSelectStop(idx: number): void {
|
||||
}
|
||||
|
||||
function _gradientRenderStopList(): void {
|
||||
const list = document.getElementById('gradient-stops-list');
|
||||
const list = _el('gradient-stops-list');
|
||||
if (!list) return;
|
||||
list.innerHTML = '';
|
||||
|
||||
@@ -305,7 +311,7 @@ function _gradientRenderStopList(): void {
|
||||
row.addEventListener('mousedown', () => _gradientSelectStop(idx));
|
||||
|
||||
// Position
|
||||
const posInput = row.querySelector('.gradient-stop-pos');
|
||||
const posInput = row.querySelector('.gradient-stop-pos')!;
|
||||
posInput.addEventListener('change', (e) => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
const val = Math.min(1, Math.max(0, parseFloat(target.value) || 0));
|
||||
@@ -316,7 +322,7 @@ function _gradientRenderStopList(): void {
|
||||
posInput.addEventListener('focus', () => _gradientSelectStop(idx));
|
||||
|
||||
// Left color
|
||||
row.querySelector('.gradient-stop-color').addEventListener('input', (e) => {
|
||||
row.querySelector('.gradient-stop-color')!.addEventListener('input', (e) => {
|
||||
const val = (e.target as HTMLInputElement).value;
|
||||
_gradientStops[idx].color = hexToRgbArray(val);
|
||||
const markers = document.querySelectorAll('.gradient-marker');
|
||||
@@ -325,7 +331,7 @@ function _gradientRenderStopList(): void {
|
||||
});
|
||||
|
||||
// Bidirectional toggle
|
||||
row.querySelector('.gradient-stop-bidir-btn').addEventListener('click', (e) => {
|
||||
row.querySelector('.gradient-stop-bidir-btn')!.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
_gradientStops[idx].colorRight = _gradientStops[idx].colorRight
|
||||
? null
|
||||
@@ -335,13 +341,13 @@ function _gradientRenderStopList(): void {
|
||||
});
|
||||
|
||||
// Right color
|
||||
row.querySelector('.gradient-stop-color-right').addEventListener('input', (e) => {
|
||||
row.querySelector('.gradient-stop-color-right')!.addEventListener('input', (e) => {
|
||||
_gradientStops[idx].colorRight = hexToRgbArray((e.target as HTMLInputElement).value);
|
||||
_gradientRenderCanvas();
|
||||
});
|
||||
|
||||
// Remove
|
||||
row.querySelector('.btn-danger').addEventListener('click', (e) => {
|
||||
row.querySelector('.btn-danger')!.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
if (_gradientStops.length > 2) {
|
||||
_gradientStops.splice(idx, 1);
|
||||
@@ -382,7 +388,7 @@ export function gradientAddStop(position?: number): void {
|
||||
/* ── Drag ─────────────────────────────────────────────────────── */
|
||||
|
||||
function _gradientStartDrag(e: MouseEvent, idx: number): void {
|
||||
const track = document.getElementById('gradient-markers-track');
|
||||
const track = _el('gradient-markers-track');
|
||||
if (!track) return;
|
||||
_gradientDragging = { idx, trackRect: track.getBoundingClientRect() };
|
||||
|
||||
@@ -442,7 +448,7 @@ export function deleteCustomGradientPreset(name: string): void {
|
||||
/* ── Track click → add stop ───────────────────────────────────── */
|
||||
|
||||
function _gradientSetupTrackClick(): void {
|
||||
const track = document.getElementById('gradient-markers-track');
|
||||
const track = _el('gradient-markers-track');
|
||||
if (!track || (track as any)._gradientClickBound) return;
|
||||
(track as any)._gradientClickBound = true;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user