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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 76 additions & 7 deletions src/core/ui/canvas-toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ export class CanvasToolbar {
// Variables elements
private variablesList: HTMLDivElement | null = null;
private variablesEmpty: HTMLDivElement | null = null;
private variablesAddForm: HTMLDivElement | null = null;
private variablesAddInput: HTMLInputElement | null = null;

// Label elements
private resolutionLabel: HTMLSpanElement | null = null;
Expand Down Expand Up @@ -234,7 +236,21 @@ export class CanvasToolbar {
<div class="ss-canvas-toolbar-popup ss-canvas-toolbar-popup--variables" data-popup="variables">
<div class="ss-canvas-toolbar-popup-header ss-variables-header">
<span>Merge Fields</span>
<button class="ss-variables-add-btn" data-action="add-variable">+</button>
<button class="ss-variables-add-btn" data-action="add-variable" type="button" aria-label="Add merge field">+</button>
</div>
<div class="ss-variables-add-form" data-variables-add-form hidden>
<input
class="ss-variable-value ss-variables-add-input"
type="text"
data-variables-add-input
placeholder="Variable name"
autocomplete="off"
spellcheck="false"
/>
<div class="ss-variables-add-actions">
<button type="button" class="ss-variables-add-confirm" data-action="confirm-add-variable">Add</button>
<button type="button" class="ss-variables-add-cancel" data-action="cancel-add-variable">Cancel</button>
</div>
</div>
<div class="ss-variables-list" data-variables-list></div>
<div class="ss-variables-empty" data-variables-empty>No merge fields defined</div>
Expand All @@ -260,6 +276,8 @@ export class CanvasToolbar {

this.variablesList = this.container.querySelector("[data-variables-list]");
this.variablesEmpty = this.container.querySelector("[data-variables-empty]");
this.variablesAddForm = this.container.querySelector("[data-variables-add-form]");
this.variablesAddInput = this.container.querySelector("[data-variables-add-input]");

this.resolutionLabel = this.container.querySelector("[data-resolution-label]");
this.fpsLabel = this.container.querySelector("[data-fps-label]");
Expand Down Expand Up @@ -309,8 +327,28 @@ export class CanvasToolbar {
// Variables - Add button
this.variablesPopup?.querySelector('[data-action="add-variable"]')?.addEventListener("click", e => {
e.stopPropagation();
this.addVariable();
this.showAddVariableForm();
});
this.variablesPopup?.querySelector('[data-action="confirm-add-variable"]')?.addEventListener("click", e => {
e.stopPropagation();
this.commitAddVariable();
});
this.variablesPopup?.querySelector('[data-action="cancel-add-variable"]')?.addEventListener("click", e => {
e.stopPropagation();
this.cancelAddVariable();
});
this.variablesAddInput?.addEventListener("keydown", e => {
e.stopPropagation();
if (e.isComposing) return;
if (e.key === "Enter") {
e.preventDefault();
this.commitAddVariable();
} else if (e.key === "Escape") {
e.preventDefault();
this.cancelAddVariable();
}
});
this.variablesAddInput?.addEventListener("click", e => e.stopPropagation());

// Resolution preset clicks
this.resolutionPopup?.querySelectorAll("[data-width]").forEach(item => {
Expand Down Expand Up @@ -590,17 +628,48 @@ export class CanvasToolbar {
});
}

private addVariable(): void {
private showAddVariableForm(): void {
if (!this.variablesAddForm || !this.variablesAddInput) return;

this.variablesAddForm.hidden = false;
if (this.variablesEmpty) {
this.variablesEmpty.style.display = "none";
}
this.variablesAddInput.value = "";
this.variablesAddInput.focus();
this.variablesAddInput.select();
}

/** Dismiss the add-variable form without registering a field. */
private cancelAddVariable(): void {
if (!this.variablesAddForm || !this.variablesAddInput) return;

this.variablesAddInput.value = "";
this.variablesAddForm.hidden = true;
// Restore empty-state visibility from current field list
this.renderVariablesList();
this.variablesPopup?.querySelector<HTMLButtonElement>('[data-action="add-variable"]')?.focus();
}

/** Register a new merge field from the in-app form; empty/whitespace is a no-op. */
private commitAddVariable(): void {
const ssEdit = this.getShotstackEdit();
if (!ssEdit) return;
if (!ssEdit || !this.variablesAddInput) return;

// eslint-disable-next-line no-alert -- Intentional use of prompt for quick variable name input
const name = prompt("Variable name:");
if (!name || !name.trim()) return;
const name = this.variablesAddInput.value;
if (!name || !name.trim()) {
this.cancelAddVariable();
return;
}

const sanitizedName = name.trim().toUpperCase().replace(/\s+/g, "_");
ssEdit.mergeFields.register({ name: sanitizedName, defaultValue: "" });
this.variablesAddInput.value = "";
if (this.variablesAddForm) {
this.variablesAddForm.hidden = true;
}
this.renderVariablesList();
this.variablesPopup?.querySelector<HTMLButtonElement>('[data-action="add-variable"]')?.focus();
}

setResolution(width: number, height: number): void {
Expand Down
49 changes: 49 additions & 0 deletions src/styles/ui/canvas-toolbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,55 @@
color: rgba(0, 0, 0, 0.8);
}

/* Add-variable form */
.ss-variables-add-form {
flex-shrink: 0;
display: flex;
flex-direction: column;
gap: 8px;
padding: 4px 8px 8px;
}

.ss-variables-add-form[hidden] {
display: none;
}

.ss-variables-add-actions {
display: flex;
gap: 6px;
justify-content: flex-end;
}

.ss-variables-add-confirm,
.ss-variables-add-cancel {
padding: 6px 10px;
border: none;
border-radius: 8px;
font-size: 12px;
font-weight: 500;
cursor: pointer;
transition: background 0.15s ease, color 0.15s ease;
}

.ss-variables-add-confirm {
background: rgba(99, 102, 241, 0.12);
color: rgba(79, 70, 229, 0.95);
}

.ss-variables-add-confirm:hover {
background: rgba(99, 102, 241, 0.2);
}

.ss-variables-add-cancel {
background: rgba(0, 0, 0, 0.05);
color: rgba(0, 0, 0, 0.55);
}

.ss-variables-add-cancel:hover {
background: rgba(0, 0, 0, 0.1);
color: rgba(0, 0, 0, 0.8);
}

/* THE scroll container - single source of scroll */
.ss-variables-list {
flex: 1 1 auto;
Expand Down
Loading