Skip to content

Instantly share code, notes, and snippets.

@realvjy
Created November 24, 2024 14:16
Show Gist options
  • Save realvjy/fe8c8754c747de07f0bf177084d68540 to your computer and use it in GitHub Desktop.
Save realvjy/fe8c8754c747de07f0bf177084d68540 to your computer and use it in GitHub Desktop.
These fuctions will help you easy managing Variable and Collection in Figma, if you creating a plugin
// Figma Variable Manager
// Authoer: vijay verma
// Twitter/X: https://x.com/realvjy
// Date: 22 Nov, 2024
interface VariableConfig {
name: string;
type: VariableResolvedDataType;
value: string | number | boolean | RGB | RGBA;
description?: string;
scopes?: VariableScope[];
collectionId?: string;
}
// Basic interfaces
interface CollectionInfo {
id: string;
name: string;
modes: { name: string; modeId: string }[]; // Keep modeId in info only for reference
}
interface CollectionConfig {
name: string;
modes?: { name: string }[];
description?: string;
}
interface ModeUpdate {
oldName: string;
newName: string;
}
export class FigmaVariablesManager {
/**
* Gets or creates a variable in the specified collection
*/
static getOrCreateVariable(
name: string,
collection: VariableCollection,
type: VariableResolvedDataType = 'COLOR'
): Variable {
try {
// Try to find existing variable
const existingVariable = figma.variables.getLocalVariables(type)
.find(v => v.name === name && v.variableCollectionId === collection.id);
if (existingVariable) {
return existingVariable;
}
// Create new if not found
return figma.variables.createVariable(name, collection as any, type);
} catch (error) {
console.error(`Error in getOrCreateVariable for ${name}:`, error);
throw error;
}
}
/**
* Creates a new variable with additional properties
*/
static async createVariable({
name,
type,
value,
description = '',
scopes = ['ALL_SCOPES'],
collectionId,
}: VariableConfig): Promise<Variable | null> {
try {
// Get or create collection if not provided
const collections = figma.variables.getLocalVariableCollections();
const collection = collectionId
? collections.find(c => c.id === collectionId)
: collections[0] || figma.variables.createVariableCollection('Default Collection');
if (!collection) {
throw new Error('Unable to find or create variable collection');
}
// Get or create the variable
const variable = this.getOrCreateVariable(name, collection, type);
// Set additional properties
const modeId = collection.defaultModeId;
variable.setValueForMode(modeId, value);
variable.description = description;
variable.scopes = scopes;
return variable;
} catch (error) {
console.error('Error creating variable:', error);
return null;
}
}
/**
* Updates an existing variable by name and collection
*/
static async updateVariable(
name: string,
updates: Partial<VariableConfig>
): Promise<Variable | null> {
try {
const type = updates.type || 'COLOR'; // Default type if not provided
const variable = figma.variables.getLocalVariables(type)
.find(v => v.name === name);
if (!variable) {
throw new Error(`Variable with name ${name} not found`);
}
// Update basic properties
if (updates.name) variable.name = updates.name;
if (updates.description) variable.description = updates.description;
if (updates.scopes) variable.scopes = updates.scopes;
// Update value if provided
if (updates.value) {
const collection = figma.variables.getLocalVariableCollections()
.find(c => c.id === variable.variableCollectionId);
if (collection) {
const modeId = collection.defaultModeId;
variable.setValueForMode(modeId, updates.value);
}
}
return variable;
} catch (error) {
console.error('Error updating variable:', error);
return null;
}
}
/**
* Deletes a variable by name and type
*/
static async deleteVariable(name: string, type: VariableResolvedDataType = 'COLOR'): Promise<boolean> {
try {
const variable = figma.variables.getLocalVariables(type)
.find(v => v.name === name);
if (!variable) {
throw new Error(`Variable with name ${name} not found`);
}
variable.remove();
return true;
} catch (error) {
console.error('Error deleting variable:', error);
return false;
}
}
/**
* Gets all variables in a collection
*/
static getCollectionVariables(collectionId: string): Variable[] {
const collection = figma.variables.getLocalVariableCollections()
.find(c => c.id === collectionId);
if (!collection) return [];
return Array.from(collection.variableIds)
.map(id =>
figma.variables.getLocalVariables()
.find(v => v.id === id)
)
.filter(Boolean) as Variable[];
}
/**
* Bulk creates variables from a config array
*/
static async bulkCreateVariables(
configs: VariableConfig[]
): Promise<(Variable | null)[]> {
const results = await Promise.all(
configs.map(config => this.createVariable(config))
);
return results;
}
}
// Example usage:
/*
// Create a new collection with multiple modes
const collection = FigmaVariablesManager.createCollection({
name: 'Theme Variables',
modes: [
{ name: 'Light', modeId: '1:0' },
{ name: 'Dark', modeId: '1:1' }
],
description: 'Theme variables for the design system'
});
// Update collection
const updatedCollection = FigmaVariablesManager.updateCollection(collection.id, {
name: 'Updated Theme',
modes: [
{ name: 'Light Mode', modeId: '1:0' },
{ name: 'Dark Mode', modeId: '1:1' },
{ name: 'High Contrast', modeId: '1:2' }
]
});
// Rename a mode
FigmaVariablesManager.renameMode(collection.id, '1:0', 'Default Light');
// Copy variables between collections
const copiedVars = FigmaVariablesManager.copyVariablesToCollection(
sourceCollectionId,
targetCollectionId,
['primary-color', 'secondary-color']
);
*/
///////// COllection Manager //////
export class CollectionsManager {
/**
* Lists all available collections with their info
*/
static listCollections(): CollectionInfo[] {
return figma.variables.getLocalVariableCollections()
.map(collection => ({
id: collection.id,
name: collection.name,
modes: collection.modes
}));
}
/**
* Gets collection by ID
*/
static findCollectionById(id: string): VariableCollection | null {
return figma.variables.getLocalVariableCollections()
.find(c => c.id === id) || null;
}
/**
* Gets collection info by name
*/
static findCollectionByName(name: string): CollectionInfo | null {
const collection = figma.variables.getLocalVariableCollections()
.find(c => c.name === name);
if (!collection) return null;
return {
id: collection.id,
name: collection.name,
modes: collection.modes
};
}
/**
* Creates a new collection with specified modes
*/
static createCollection({
name,
modes = [],
description = ''
}: CollectionConfig): VariableCollection {
try {
// Check for existing collection
const existingCollection = this.findCollectionByName(name);
if (existingCollection) {
throw new Error(`Collection with name "${name}" already exists`);
}
// Create collection (comes with default "Mode 1")
const collection = figma.variables.createVariableCollection(name);
if (description) {
// @ts-ignore - Description might be supported in future
collection.description = description;
}
// Rename default mode if first mode provided
if (modes.length > 0) {
collection.renameMode(collection.defaultModeId, modes[0].name);
}
// Add additional modes
modes.slice(1).forEach(mode => {
collection.addMode(mode.name);
});
return collection;
} catch (error) {
console.error('Error creating collection:', error);
throw error;
}
}
/**
* Gets or creates a collection
*/
static getOrCreateCollection(config: CollectionConfig): VariableCollection {
const existingCollection = this.findCollectionByName(config.name);
if (existingCollection) {
return this.findCollectionById(existingCollection.id)!;
}
return this.createCollection(config);
}
/**
* Updates a collection by name
*/
static updateCollectionByName(
name: string,
updates: Partial<CollectionConfig>
): VariableCollection | null {
const collection = this.findCollectionByName(name);
if (!collection) return null;
return this.updateCollection(collection.id, updates);
}
/**
* Updates an existing collection by ID
*/
static updateCollection(
collectionId: string,
updates: Partial<CollectionConfig>
): VariableCollection | null {
try {
const collection = this.findCollectionById(collectionId);
if (!collection) {
throw new Error(`Collection with ID ${collectionId} not found`);
}
// Update name if provided
if (updates.name) {
collection.name = updates.name;
}
// Update description if provided
if (updates.description) {
// @ts-ignore - Description might be supported in future
collection.description = updates.description;
}
// Update modes if provided
if (updates.modes?.length) {
// Rename default mode to first mode name
collection.renameMode(collection.defaultModeId, updates.modes[0].name);
// Remove all non-default modes
collection.modes.forEach(mode => {
if (mode.modeId !== collection.defaultModeId) {
collection.removeMode(mode.modeId);
}
});
// Add remaining modes
updates.modes.slice(1).forEach(mode => {
collection.addMode(mode.name);
});
}
return collection;
} catch (error) {
console.error('Error updating collection:', error);
return null;
}
}
/**
* Deletes a collection by name
*/
static deleteCollectionByName(name: string, force = false): boolean {
const collection = this.findCollectionByName(name);
if (!collection) return false;
return this.deleteCollection(collection.id, force);
}
/**
* Deletes a collection by ID
*/
static deleteCollection(collectionId: string, force = false): boolean {
try {
const collection = this.findCollectionById(collectionId);
if (!collection) {
throw new Error(`Collection with ID ${collectionId} not found`);
}
if (!force && figma.variables.getLocalVariables()
.some(v => v.variableCollectionId === collectionId)) {
throw new Error('Collection contains variables. Use force=true to delete anyway');
}
collection.remove();
return true;
} catch (error) {
console.error('Error deleting collection:', error);
return false;
}
}
}
// Usage example:
/*
// Create a new collection
const themeCollection = CollectionsManager.createCollection({
name: 'Theme',
modes: [
{ name: 'Light', modeId: '1:0' },
{ name: 'Dark', modeId: '1:1' }
],
description: 'Theme variables for the design system'
});
// Get or create a collection
const systemCollection = CollectionsManager.getOrCreateCollection({
name: 'Design System',
modes: [
{ name: 'Default', modeId: '1:0' },
{ name: 'High Contrast', modeId: '1:1' }
]
});
// Update collection with new modes and renames
const updatedCollection = CollectionsManager.updateCollectionByName('Theme', {
modes: [
{ name: 'Light Mode', modeId: '1:0' },
{ name: 'Dark Mode', modeId: '1:1' },
{ name: 'System', modeId: '1:2' }
],
renameMode: [
{ oldName: 'Light', newName: 'Light 2.0' },
{ oldName: 'Dark', newName: 'Dark 2.0' }
],
defaultModeId: '1:0'
});
// List all collections
const collections = CollectionsManager.listCollections();
console.log('Available collections:', collections);
// Delete a collection
const deleted = CollectionsManager.deleteCollectionByName('Theme', true);
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment