diff --git a/src/components/APIExplorer.tsx b/src/components/APIExplorer.tsx index 965851f..cd3a707 100644 --- a/src/components/APIExplorer.tsx +++ b/src/components/APIExplorer.tsx @@ -130,15 +130,18 @@ const APIExplorer: React.FC = ({ if ( typeof value === 'object' && value !== null && - !('type' in value) + !('type' in value) && + typeof value !== 'function' ) { return { value: key, label: key.replace(/([A-Z])/g, ' $1').trim(), - subcategories: Object.keys(value || {}).map((subKey) => ({ - value: subKey, - label: subKey.replace(/([A-Z])/g, ' $1').trim(), - })), + subcategories: Object.keys(value || {}).map((subKey) => { + const entry = (value as any)[subKey] + const label = + entry?.displayName ?? subKey.replace(/([A-Z])/g, ' $1').trim() + return { value: subKey, label } + }), } } return { @@ -172,9 +175,10 @@ const APIExplorer: React.FC = ({ ? selectedFunctionName.split('.') : [null, selectedFunctionName] const topCategory = examples[selectedExampleCategory] - const funcToExecute = category + const rawEntry = category ? topCategory[category][funcName] : topCategory[selectedFunctionName] + const funcToExecute = rawEntry?.code ?? rawEntry if (funcToExecute) { const originalConsole = { ...console } diff --git a/src/examples/assets.ts b/src/examples/assets.ts index 3e25083..39e3860 100644 --- a/src/examples/assets.ts +++ b/src/examples/assets.ts @@ -118,194 +118,230 @@ export enum ValidFileTypesEnum { export const Assets = { // Asset Management assetManagement: { - getAllAssets: async () => { - // Get all assets - const assets = await webflow.getAllAssets() - - // Loop to list assets in the console - for (const asset of assets) { - const name = await asset.getName() - const mimeType = await asset.getMimeType() - console.log(name, mimeType) - } + getAllAssets: { + displayName: 'Get all assets', + code: async () => { + // Get all assets + const assets = await webflow.getAllAssets() + + // Loop to list assets in the console + for (const asset of assets) { + const name = await asset.getName() + const mimeType = await asset.getMimeType() + console.log(name, mimeType) + } + }, }, - getAssetById: async (asset_id: string) => { - const asset = await webflow.getAssetById(asset_id) - console.log('check') - console.log(asset) + getAssetById: { + displayName: 'Get asset by ID', + code: async (asset_id: string) => { + const asset = await webflow.getAssetById(asset_id) + console.log('check') + console.log(asset) + }, }, - getAssetName: async () => { - // Get Selected Element - const el = await webflow.getSelectedElement() - - // Check if element is selected and its type - if (!el || el.type !== 'Image') { - console.error('Please select an Image element on the canvas') - await webflow.notify({ - type: 'Error', - message: 'Please select an Image element on the canvas', - }) - } else { - const asset = await el.getAsset() // Get Asset - const assetName = await asset?.getName() // Get Asset Name - console.log(`Asset Name: ${assetName}`) - } + getAssetName: { + displayName: 'Get asset name', + code: async () => { + // Get Selected Element + const el = await webflow.getSelectedElement() + + // Check if element is selected and its type + if (!el || el.type !== 'Image') { + console.error('Please select an Image element on the canvas') + await webflow.notify({ + type: 'Error', + message: 'Please select an Image element on the canvas', + }) + } else { + const asset = await el.getAsset() // Get Asset + const assetName = await asset?.getName() // Get Asset Name + console.log(`Asset Name: ${assetName}`) + } + }, }, - getAssetMimeType: async () => { - // Get Selected Element - const el = await webflow.getSelectedElement() - - // Check if element is selected and its type - if (!el || el.type !== 'Image') { - console.error('Please select an Image element on the canvas') - await webflow.notify({ - type: 'Error', - message: 'Please select an Image element on the canvas', - }) - } else { - const asset = await el.getAsset() // Get Asset - const assetMimeType = await asset?.getMimeType() // Get Asset MIME Type - console.log(`Asset MIME type: ${assetMimeType}`) - } + getAssetMimeType: { + displayName: 'Get asset MIME type', + code: async () => { + // Get Selected Element + const el = await webflow.getSelectedElement() + + // Check if element is selected and its type + if (!el || el.type !== 'Image') { + console.error('Please select an Image element on the canvas') + await webflow.notify({ + type: 'Error', + message: 'Please select an Image element on the canvas', + }) + } else { + const asset = await el.getAsset() // Get Asset + const assetMimeType = await asset?.getMimeType() // Get Asset MIME Type + console.log(`Asset MIME type: ${assetMimeType}`) + } + }, }, - getAssetURL: async (assetId: string) => { - // Get Asset by ID - const asset = await webflow.getAssetById(assetId) - console.log(asset) + getAssetURL: { + displayName: 'Get asset URL', + code: async (assetId: string) => { + // Get Asset by ID + const asset = await webflow.getAssetById(assetId) + console.log(asset) - if (asset) { - // Get asset URL - const url = await asset.getUrl() - console.log(`Asset URL: ${url}`) - } + if (asset) { + // Get asset URL + const url = await asset.getUrl() + console.log(`Asset URL: ${url}`) + } + }, }, }, // Asset Creation assetCreation: { - createAssetFromFileUpload: async (file: File) => { - if (file) { - const asset = await webflow.createAsset(file) + createAssetFromFileUpload: { + displayName: 'Create asset from file upload', + code: async (file: File) => { + if (file) { + const asset = await webflow.createAsset(file) - console.log(`Asset ID: ${asset.id}`) - } + console.log(`Asset ID: ${asset.id}`) + } + }, }, - createAssetFromURL: async ( - url: string, - fileName: string, - fileTypeEnum: ValidFileTypesEnum, - ) => { - // Fetch image from remote source and build a Blob object - const response = await fetch(url) - const blob = await response.blob() - const file = new File([blob], fileName, { - type: fileTypeEnum, - }) - - console.log('file', file) - - try { - // Create and upload the asset to webflow - const asset = await webflow.createAsset(file) - console.log(asset) - } catch (err) { - const error = err as { cause: { tag: string }; message: string } - console.error(`Cause:${error.cause.tag}`) - console.error(`Cause:${error.message}`) - } + createAssetFromURL: { + displayName: 'Create asset from URL', + code: async ( + url: string, + fileName: string, + fileTypeEnum: ValidFileTypesEnum, + ) => { + // Fetch image from remote source and build a Blob object + const response = await fetch(url) + const blob = await response.blob() + const file = new File([blob], fileName, { + type: fileTypeEnum, + }) + + console.log('file', file) + + try { + // Create and upload the asset to webflow + const asset = await webflow.createAsset(file) + console.log(asset) + } catch (err) { + const error = err as { cause: { tag: string }; message: string } + console.error(`Cause:${error.cause.tag}`) + console.error(`Cause:${error.message}`) + } + }, }, }, // Alt Text altText: { - getAltText: async (asset: Asset) => { - // Now asset is the actual Asset object, not wrapped - if (asset) { - // Get asset alt text - const altText = await asset.getAltText() - console.log(`Asset Alt Text: ${altText}`) - } + getAltText: { + displayName: 'Get alt text', + code: async (asset: Asset) => { + // Now asset is the actual Asset object, not wrapped + if (asset) { + // Get asset alt text + const altText = await asset.getAltText() + console.log(`Asset Alt Text: ${altText}`) + } + }, }, - setAltText: async (assetId: string, altText: string) => { - // Get Asset by ID - const asset = await webflow.getAssetById(assetId) - console.log(asset) - - if (asset) { - // Get asset URL - const originalAltText = await asset.getAltText() - await asset.setAltText(altText) - const newAltText = await asset.getAltText() - console.log(`Original Asset Alt Text: ${originalAltText}`) - console.log(`New Asset Alt Text: ${newAltText}`) - } + setAltText: { + displayName: 'Set alt text', + code: async (assetId: string, altText: string) => { + // Get Asset by ID + const asset = await webflow.getAssetById(assetId) + console.log(asset) + + if (asset) { + // Get asset URL + const originalAltText = await asset.getAltText() + await asset.setAltText(altText) + const newAltText = await asset.getAltText() + console.log(`Original Asset Alt Text: ${originalAltText}`) + console.log(`New Asset Alt Text: ${newAltText}`) + } + }, }, }, // Canvas canvas: { - addAssetToCanvas: async (assetId: string) => { - // Get Asset URL - const asset = await webflow.getAssetById(assetId) - const assetUrl = await asset?.getUrl() - - // Get selected element - const selectedElement = await webflow.getSelectedElement() - if (!selectedElement) { - webflow.notify({ type: 'Error', message: 'Please select an element' }) - return - } - - // Add DOM element with an image tag to selected element - if (selectedElement.children && assetUrl) { - const domElement = await selectedElement.append( - webflow.elementPresets.DOM, - ) - await domElement.setTag('img') - await domElement.setAttribute('src', assetUrl) - } + addAssetToCanvas: { + displayName: 'Add asset to canvas', + code: async (assetId: string) => { + // Get Asset URL + const asset = await webflow.getAssetById(assetId) + const assetUrl = await asset?.getUrl() + + // Get selected element + const selectedElement = await webflow.getSelectedElement() + if (!selectedElement) { + webflow.notify({ type: 'Error', message: 'Please select an element' }) + return + } + + // Add DOM element with an image tag to selected element + if (selectedElement.children && assetUrl) { + const domElement = await selectedElement.append( + webflow.elementPresets.DOM, + ) + await domElement.setTag('img') + await domElement.setAttribute('src', assetUrl) + } + }, }, }, // Asset Folders assetFolders: { - getAllAssetFolders: async () => { - const folders = await webflow.getAllAssetFolders() - console.log(folders) + getAllAssetFolders: { + displayName: 'Get all asset folders', + code: async () => { + const folders = await webflow.getAllAssetFolders() + console.log(folders) + }, }, - createAssetFolder: async (name: string, parentFolderName?: string) => { - // Get All Asset Folders - const folders = await webflow.getAllAssetFolders() - - // Find Parent Folder by Name - if (parentFolderName) { - const parentFolder = await Promise.all( - folders.map(async (folder) => { - const folderName = await folder.getName() - if (folderName === parentFolderName) { - return folder - } - return null - }), - ).then((results) => results.find((folder) => folder !== null)) - - // Create Asset Folder with parent folder - if (parentFolder) { - const newFolder = await webflow.createAssetFolder(name, parentFolder.id) + createAssetFolder: { + displayName: 'Create asset folder', + code: async (name: string, parentFolderName?: string) => { + // Get All Asset Folders + const folders = await webflow.getAllAssetFolders() + + // Find Parent Folder by Name + if (parentFolderName) { + const parentFolder = await Promise.all( + folders.map(async (folder) => { + const folderName = await folder.getName() + if (folderName === parentFolderName) { + return folder + } + return null + }), + ).then((results) => results.find((folder) => folder !== null)) + + // Create Asset Folder with parent folder + if (parentFolder) { + const newFolder = await webflow.createAssetFolder(name, parentFolder.id) + console.log(newFolder) + } + } else { + // Create Asset Folder + const newFolder = await webflow.createAssetFolder(name) console.log(newFolder) } - } else { - // Create Asset Folder - const newFolder = await webflow.createAssetFolder(name) - console.log(newFolder) - } + }, }, }, } diff --git a/src/examples/components.ts b/src/examples/components.ts index 68390b3..4ed54eb 100644 --- a/src/examples/components.ts +++ b/src/examples/components.ts @@ -35,712 +35,750 @@ export const getComponentByName = async ( export const Components = { // Component Management componentManagement: { - getAllComponents: async () => { - // Get all components - const components = await webflow.getAllComponents() - - // Print Component Details - if (components.length > 0) { - console.log('List of registered components:') - - for (let component in components) { - const currentComponentName = await components[component].getName() - console.log( - `${component + 1}. Component Name: ${currentComponentName}, Component ID: ${components[component].id}`, - ) + getAllComponents: { + displayName: 'Get all components', + code: async () => { + // Get all components + const components = await webflow.getAllComponents() + + // Print Component Details + if (components.length > 0) { + console.log('List of registered components:') + + for (let component in components) { + const currentComponentName = await components[component].getName() + console.log( + `${component + 1}. Component Name: ${currentComponentName}, Component ID: ${components[component].id}`, + ) + } + } else { + console.log('No components are currently registered.') } - } else { - console.log('No components are currently registered.') - } + }, }, - getComponentByName: async () => { - // Fetch a component by name only - const heroSection = await webflow.getComponentByName('Hero'); - console.log(heroSection.id); + getComponentByName: { + displayName: 'Get component by name', + code: async () => { + // Fetch a component by name only + const heroSection = await webflow.getComponentByName('Hero'); + console.log(heroSection.id); + }, }, - getComponentByNameAndGroup: async () => { - // Fetch a component scoped to a group - const marketingHero = await webflow.getComponentByName('Marketing', 'Hero'); - console.log(marketingHero.id); + getComponentByNameAndGroup: { + displayName: 'Get component by name and group', + code: async () => { + // Fetch a component scoped to a group + const marketingHero = await webflow.getComponentByName('Marketing', 'Hero'); + console.log(marketingHero.id); + }, }, - getCurrentComponent: async () => { - // Get the component currently being edited - const component = await webflow.getCurrentComponent(); + getCurrentComponent: { + displayName: 'Get current component', + code: async () => { + // Get the component currently being edited + const component = await webflow.getCurrentComponent(); - if (component) { - const name = await component.getName(); - console.log(`Currently editing component: ${name}`); + if (component) { + const name = await component.getName(); + console.log(`Currently editing component: ${name}`); - // Get all elements inside the active component - const root = await component.getRootElement(); - const children = await root.getChildren(); - console.log(`Root has ${children.length} child element(s)`); - } else { - console.log('Not currently editing a component.'); - } + // Get all elements inside the active component + const root = await component.getRootElement(); + const children = await root.getChildren(); + console.log(`Root has ${children.length} child element(s)`); + } else { + console.log('Not currently editing a component.'); + } + }, }, - searchComponents: async () => { - const heroes = await webflow.searchComponents({ q: 'Hero' }); - console.log(heroes); + searchComponents: { + displayName: 'Search components', + code: async () => { + const heroes = await webflow.searchComponents({ q: 'Hero' }); + console.log(heroes); + }, }, - getSettings: async () => { - // Get the first component's settings - const component = (await webflow.getAllComponents())[0] - const settings = await component.getSettings() - console.log(settings) - /* - { - name: 'Hero Section', - group: 'Sections', - description: 'A reusable hero with heading and CTA' - } - */ + getSettings: { + displayName: 'Get settings', + code: async () => { + // Get the first component's settings + const component = (await webflow.getAllComponents())[0] + const settings = await component.getSettings() + console.log(settings) + /* + { + name: 'Hero Section', + group: 'Sections', + description: 'A reusable hero with heading and CTA' + } + */ + }, }, - setSettings: async () => { - const component = (await webflow.getAllComponents())[0] - - // Update only the description - await component.setSettings({ - description: 'Updated hero layout with video background', - }) + setSettings: { + displayName: 'Set settings', + code: async () => { + const component = (await webflow.getAllComponents())[0] - // Move to a different group - await component.setSettings({ group: 'Legacy' }) + // Update only the description + await component.setSettings({ + description: 'Updated hero layout with video background', + }) - // Update everything at once - await component.setSettings({ - name: 'Hero Section v2', - group: 'Sections', - description: 'Redesigned hero component', - }) - }, + // Move to a different group + await component.setSettings({ group: 'Legacy' }) - getInstanceCount: async () => { - // Audit component usage across the site - const components = await webflow.getAllComponents(); - for (const component of components) { - const name = await component.getName(); - const count = await component.getInstanceCount(); - console.log(`${name}: ${count} instances`); - } - // Guard against removing a component that's still in use - const hero = components[0]; - const instanceCount = await hero.getInstanceCount(); - if (instanceCount > 0) { - console.log(`Cannot safely remove — ${instanceCount} instances exist`); - } else { - await webflow.unregisterComponent(hero); - } + // Update everything at once + await component.setSettings({ + name: 'Hero Section v2', + group: 'Sections', + description: 'Redesigned hero component', + }) + }, + }, + + getInstanceCount: { + displayName: 'Get instance count', + code: async () => { + // Audit component usage across the site + const components = await webflow.getAllComponents(); + for (const component of components) { + const name = await component.getName(); + const count = await component.getInstanceCount(); + console.log(`${name}: ${count} instances`); + } + // Guard against removing a component that's still in use + const hero = components[0]; + const instanceCount = await hero.getInstanceCount(); + if (instanceCount > 0) { + console.log(`Cannot safely remove — ${instanceCount} instances exist`); + } else { + await webflow.unregisterComponent(hero); + } + }, }, - getRootElement: async () => { - // Get Component - const all = await webflow.getAllComponents() - const firstComponent = all[0] + getRootElement: { + displayName: 'Get root element', + code: async () => { + // Get Component + const all = await webflow.getAllComponents() + const firstComponent = all[0] - // Get Root Element of Component - const root = await firstComponent?.getRootElement() - console.log(root) + // Get Root Element of Component + const root = await firstComponent?.getRootElement() + console.log(root) + }, }, - getName: async (name: string) => { - const components = await webflow.getAllComponents() + getName: { + displayName: 'Get name', + code: async (name: string) => { + const components = await webflow.getAllComponents() - // Check if component exists - for (const c in components) { - const currentComponentName = await components[c].getName() - if (name === currentComponentName) { - console.log(`Found ${name} Component`) + // Check if component exists + for (const c in components) { + const currentComponentName = await components[c].getName() + if (name === currentComponentName) { + console.log(`Found ${name} Component`) + } } - } + }, }, - setName: async () => { - // Get Component - const components = await webflow.getAllComponents() - const myComponent = components[0] + setName: { + displayName: 'Set name', + code: async () => { + // Get Component + const components = await webflow.getAllComponents() + const myComponent = components[0] - // Set Component Name - await myComponent.setName('My New Component Name') + // Set Component Name + await myComponent.setName('My New Component Name') + }, }, - createComponent: async () => { - // Get selected element - const rootElement = await webflow.getSelectedElement() + createComponent: { + displayName: 'Create component', + code: async () => { + // Get selected element + const rootElement = await webflow.getSelectedElement() - if (rootElement) { - // Create a component from the Root Element - const component = await webflow.registerComponent( - 'MyCustomComponent', - rootElement, - ) - console.log(`Component registered with ID: ${component.id}`) - } else { - console.log( - 'No element is currently selected. Please select a root element first.', - ) - } + if (rootElement) { + // Create a component from the Root Element + const component = await webflow.registerComponent( + 'MyCustomComponent', + rootElement, + ) + console.log(`Component registered with ID: ${component.id}`) + } else { + console.log( + 'No element is currently selected. Please select a root element first.', + ) + } + }, }, - createComponentWithoutRoot: async () => { - // Create a hero component in the Sections group that is not within an existing element - const hero = await webflow.registerComponent({ - name: 'Hero Section', - group: 'Sections', - description: 'A reusable hero section with heading and CTA', - }); - console.log(`Component registered with ID: ${hero.id}`) - }, - - createComponentFromElement: async () => { - // Convert an existing element into a component, by default replacing the element with the new component - const selectedElement = await webflow.getSelectedElement() - if (selectedElement) { - const heroComponent = await webflow.registerComponent( - { - name: 'Hero Section', - group: 'Sections', - description: 'Main hero with heading and CTA' - }, - selectedElement - ) - } + createComponentWithoutRoot: { + displayName: 'Create component without root', + code: async () => { + // Create a hero component in the Sections group that is not within an existing element + const hero = await webflow.registerComponent({ + name: 'Hero Section', + group: 'Sections', + description: 'A reusable hero section with heading and CTA', + }); + console.log(`Component registered with ID: ${hero.id}`) + }, + }, + + createComponentFromElement: { + displayName: 'Create component from element', + code: async () => { + // Convert an existing element into a component, by default replacing the element with the new component + const selectedElement = await webflow.getSelectedElement() + if (selectedElement) { + const heroComponent = await webflow.registerComponent( + { + name: 'Hero Section', + group: 'Sections', + description: 'Main hero with heading and CTA' + }, + selectedElement + ) + } + }, }, - duplicateComponent: async () => { - // Duplicate a component - const [original] = await webflow.getAllComponents() - const copy = await webflow.registerComponent({ name: 'Card Copy' }, original) + duplicateComponent: { + displayName: 'Duplicate component', + code: async () => { + // Duplicate a component + const [original] = await webflow.getAllComponents() + const copy = await webflow.registerComponent({ name: 'Card Copy' }, original) + }, }, - deleteComponent: async () => { - // Get selected element - const selectedElement = await webflow.getSelectedElement() + deleteComponent: { + displayName: 'Delete component', + code: async () => { + // Get selected element + const selectedElement = await webflow.getSelectedElement() - if (selectedElement) { - // Create component from selected element - const myNewComponent = await webflow.registerComponent( - 'Hero Component', - selectedElement, - ) + if (selectedElement) { + // Create component from selected element + const myNewComponent = await webflow.registerComponent( + 'Hero Component', + selectedElement, + ) - // Delete Component - await webflow.unregisterComponent(myNewComponent) - } else { - console.log( - 'No element is currently selected. Please select a root element first.', - ) - } + // Delete Component + await webflow.unregisterComponent(myNewComponent) + } else { + console.log( + 'No element is currently selected. Please select a root element first.', + ) + } + }, }, }, // Component Canvas componentCanvas: { - openComponentCanvas: async () => { - // Open the canvas for the Component that has an instance selected in the Designer - const selected = await webflow.getSelectedElement(); - if (selected?.type === 'ComponentInstance') { - await webflow.openCanvas(selected); - } + openComponentCanvas: { + displayName: 'Open component canvas', + code: async () => { + // Open the canvas for the Component that has an instance selected in the Designer + const selected = await webflow.getSelectedElement(); + if (selected?.type === 'ComponentInstance') { + await webflow.openCanvas(selected); + } + }, }, - selectComponent: async () => { - // Step 1: Fetch the currently selected element - const selectedElement = await webflow.getSelectedElement() + selectComponent: { + displayName: 'Select component', + code: async () => { + // Step 1: Fetch the currently selected element + const selectedElement = await webflow.getSelectedElement() - if (selectedElement && selectedElement.type === 'ComponentInstance') { - // Step 2: Enter the context of the selected ComponentElement - await webflow.enterComponent(selectedElement as ComponentElement) - console.log('Successfully entered the component context.') + if (selectedElement && selectedElement.type === 'ComponentInstance') { + // Step 2: Enter the context of the selected ComponentElement + await webflow.enterComponent(selectedElement as ComponentElement) + console.log('Successfully entered the component context.') - // Step 3: After entering the component's context, fetch the root element - const rootElement = await webflow.getRootElement() - if (rootElement) { - console.log('Root element of the component:', rootElement) + // Step 3: After entering the component's context, fetch the root element + const rootElement = await webflow.getRootElement() + if (rootElement) { + console.log('Root element of the component:', rootElement) + } else { + console.log('No root element found in this component context.') + } } else { - console.log('No root element found in this component context.') + console.log('The selected element is not a ComponentElement.') } - } else { - console.log('The selected element is not a ComponentElement.') - } + }, }, - editComponent: async () => { - // Get Component - const all = await webflow.getAllComponents() - const firstComponent = all[0] + editComponent: { + displayName: 'Edit component', + code: async () => { + // Get Component + const all = await webflow.getAllComponents() + const firstComponent = all[0] - // Get Root Element on the Component - const root = (await firstComponent?.getRootElement()) as AnyElement + // Get Root Element on the Component + const root = (await firstComponent?.getRootElement()) as AnyElement - if (root.children) { - // Append DIV block to Root element - await root?.append('div') - } + if (root.children) { + // Append DIV block to Root element + await root?.append('div') + } + }, }, - exitComponent: async () => { - await webflow.exitComponent() - const rootElement = await webflow.getRootElement() - const rootElementType = rootElement?.type + exitComponent: { + displayName: 'Exit component', + code: async () => { + await webflow.exitComponent() + const rootElement = await webflow.getRootElement() + const rootElementType = rootElement?.type - // Print Root Element Type. If element type is Body, the designer has exited out of the Component context - console.log(`Element Type: ${rootElementType}`) + // Print Root Element Type. If element type is Body, the designer has exited out of the Component context + console.log(`Element Type: ${rootElementType}`) + }, }, }, // Component Instances componentInstances: { - createComponentInstance: async () => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - // Get Component - const allComponents = await webflow.getAllComponents() - const firstComponent = allComponents[0] - - // Add Component instance onto a page - await selectedElement?.before(firstComponent) - }, - - getComponent: async () => { - // Select Component Element on Page - const elements = await webflow.getAllElements() - const componentInstance = elements?.find( - (el) => el.type === 'ComponentInstance', - ) - - if (componentInstance?.type === 'ComponentInstance') { - // Get Component object from instance - const component = await componentInstance?.getComponent() - const componentName = await component?.getName() - console.log(componentName) - } else { - console.log('No component element found') - } + createComponentInstance: { + displayName: 'Create component instance', + code: async () => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() + + // Get Component + const allComponents = await webflow.getAllComponents() + const firstComponent = allComponents[0] + + // Add Component instance onto a page + await selectedElement?.before(firstComponent) + }, + }, + + getComponent: { + displayName: 'Get component', + code: async () => { + // Select Component Element on Page + const elements = await webflow.getAllElements() + const componentInstance = elements?.find( + (el) => el.type === 'ComponentInstance', + ) + + if (componentInstance?.type === 'ComponentInstance') { + // Get Component object from instance + const component = await componentInstance?.getComponent() + const componentName = await component?.getName() + console.log(componentName) + } else { + console.log('No component element found') + } + }, }, }, // Variants variants: { - getVariants: async () => { - const component = (await webflow.getAllComponents())[0] - const variants = await component.getVariants() - console.log(variants) - // [ - // { id: 'base', name: 'Primary', isSelected: true }, - // { id: 'xxxx', name: 'Secondary', isSelected: false }, - // ] - // Find which variant the user is currently editing - const activeVariant = variants.find(v => v.isSelected) - console.log(`Currently editing: ${activeVariant?.name}`) - }, - - getVariantById: async () => { - const component = await webflow.getCurrentComponent() - - if (component) { - // Get a specific variant by ID - const variant = await component.getVariant('variant-123') - console.log(variant) - /* - { - id: 'variant-123', - name: 'Secondary Hero', - isSelected: true, + getVariants: { + displayName: 'Get variants', + code: async () => { + const component = (await webflow.getAllComponents())[0] + const variants = await component.getVariants() + console.log(variants) + // [ + // { id: 'base', name: 'Primary', isSelected: true }, + // { id: 'xxxx', name: 'Secondary', isSelected: false }, + // ] + // Find which variant the user is currently editing + const activeVariant = variants.find(v => v.isSelected) + console.log(`Currently editing: ${activeVariant?.name}`) + }, + }, + + getVariantById: { + displayName: 'Get variant by ID', + code: async () => { + const component = await webflow.getCurrentComponent() + + if (component) { + // Get a specific variant by ID + const variant = await component.getVariant('variant-123') + console.log(variant) + + // Get the base variant + const base = await component.getVariant('base') + console.log(base) } - */ - - // Get the base variant - const base = await component.getVariant('base') - console.log(base) - /* - { - id: 'base', - name: 'Primary', - isSelected: false, + }, + }, + + getSelectedVariant: { + displayName: 'Get selected variant', + code: async () => { + const heroComponent = webflow.getComponentByName('hero') + // When no variant is explicitly selected, returns base + const base = await heroComponent.getSelectedVariant() + console.log(JSON.stringify(base)) + }, + }, + + createVariant: { + displayName: 'Create variant', + code: async () => { + const component = await webflow.getCurrentComponent() + + if (component) { + // Create a variant and select it immediately + const variant = await component.createVariant({ + name: 'Secondary Hero', + isSelected: true, + }) + console.log(variant) + // { id: 'variant-123', name: 'Secondary Hero', isSelected: true } + + // Name conflicts auto-increment + const variant2 = await component.createVariant({ name: 'Secondary Hero' }) + console.log(variant2.name) // 'Secondary Hero 2' } - */ - } + }, }, - getSelectedVariant: async () => { - const heroComponent = webflow.getComponentByName('hero') - // When no variant is explicitly selected, returns base - const base = await heroComponent.getSelectedVariant() - console.log(JSON.stringify(base)) - /* - { - id: 'base', - name: 'Primary', - isSelected: true, - } - */ - }, - - createVariant: async () => { - const component = await webflow.getCurrentComponent() - - if (component) { - // Create a variant and select it immediately - const variant = await component.createVariant({ - name: 'Secondary Hero', - isSelected: true, - }) - console.log(variant) - // { id: 'variant-123', name: 'Secondary Hero', isSelected: true } - - // Name conflicts auto-increment - const variant2 = await component.createVariant({ name: 'Secondary Hero' }) - console.log(variant2.name) // 'Secondary Hero 2' - } - }, - - duplicateVariant: async () => { - const component = await webflow.getCurrentComponent() + duplicateVariant: { + displayName: 'Duplicate variant', + code: async () => { + const component = await webflow.getCurrentComponent() - if (component) { - // Get the selected variant or the base variant as default - const selectedVariant = await component.getSelectedVariant() + if (component) { + // Get the selected variant or the base variant as default + const selectedVariant = await component.getSelectedVariant() - // Duplicate the selected variant - const duplicateVariant = await component.createVariant({ - name: 'Duplicate of Secondary Hero', - isSelected: true, - }, selectedVariant.id) - console.log(duplicateVariant.name) // 'Duplicate of Secondary Hero' - } + // Duplicate the selected variant + const duplicateVariant = await component.createVariant({ + name: 'Duplicate of Secondary Hero', + isSelected: true, + }, selectedVariant.id) + console.log(duplicateVariant.name) // 'Duplicate of Secondary Hero' + } + }, }, - setVariantSettings: async () => { - const component = await webflow.getCurrentComponent() + setVariantSettings: { + displayName: 'Set variant settings', + code: async () => { + const component = await webflow.getCurrentComponent() - if (component) { - const variant = await component.setVariant({ - id: 'variant-123', - name: 'Primary Hero', - }) - console.log(variant) - } + if (component) { + const variant = await component.setVariant({ + id: 'variant-123', + name: 'Primary Hero', + }) + console.log(variant) + } + }, }, - deleteVariant: async () => { - const component = await webflow.getCurrentComponent() + deleteVariant: { + displayName: 'Delete variant', + code: async () => { + const component = await webflow.getCurrentComponent() - if (component) { - await component.deleteVariant({ id: 'variant-456' }) - } + if (component) { + await component.deleteVariant({ id: 'variant-456' }) + } + }, }, - reorderVariants: async () => { - const component = await webflow.getCurrentComponent() + reorderVariants: { + displayName: 'Reorder variants', + code: async () => { + const component = await webflow.getCurrentComponent() - if (component) { - // Before: base, variant-1, variant-2, variant-3 - await component.reorderVariants(['variant-1', 'variant-3']) - // After: base, variant-1, variant-3, variant-2 - } + if (component) { + // Before: base, variant-1, variant-2, variant-3 + await component.reorderVariants(['variant-1', 'variant-3']) + // After: base, variant-1, variant-3, variant-2 + } + }, }, }, // Props props: { - createProp: async () => { - const component = await webflow.getCurrentComponent() - - if (component) { - const headingProp = await component.createProp({ - type: 'textContent', - name: 'Heading', - group: 'Content', - defaultValue: 'Welcome to our site', - tooltip: 'The main heading displayed in the hero section', - }) - console.log(headingProp) - } - }, + createProp: { + displayName: 'Create prop', + code: async () => { + const component = await webflow.getCurrentComponent() - createProps: async () => { - const component = await webflow.getCurrentComponent() - - if (component) { - const props = await component.createProps([ - { + if (component) { + const headingProp = await component.createProp({ type: 'textContent', name: 'Heading', group: 'Content', defaultValue: 'Welcome to our site', - }, - { - type: 'textContent', - name: 'Subheading', - group: 'Content', - defaultValue: 'We build things', - }, - { - type: 'imageAsset', - name: 'Background Image', - group: 'Content', - }, - { - type: 'link', - name: 'CTA Link', - group: 'Content', - defaultValue: { - mode: 'url', - to: 'https://example.com/signup', - openInNewTab: true, + tooltip: 'The main heading displayed in the hero section', + }) + console.log(headingProp) + } + }, + }, + + createProps: { + displayName: 'Create props', + code: async () => { + const component = await webflow.getCurrentComponent() + + if (component) { + const props = await component.createProps([ + { + type: 'textContent', + name: 'Heading', + group: 'Content', + defaultValue: 'Welcome to our site', }, - }, - { - type: 'number', - name: 'Overlay Opacity', - group: 'Settings', - min: 0, - max: 100, - precision: 0, - defaultValue: 50, - }, - ]) - console.log(props) - } + { + type: 'textContent', + name: 'Subheading', + group: 'Content', + defaultValue: 'We build things', + }, + { + type: 'imageAsset', + name: 'Background Image', + group: 'Content', + }, + { + type: 'link', + name: 'CTA Link', + group: 'Content', + defaultValue: { + mode: 'url', + to: 'https://example.com/signup', + openInNewTab: true, + }, + }, + { + type: 'number', + name: 'Overlay Opacity', + group: 'Settings', + min: 0, + max: 100, + precision: 0, + defaultValue: 50, + }, + ]) + console.log(props) + } + }, }, - getProps: async () => { - const component = await webflow.getCurrentComponent() + getProps: { + displayName: 'Get props', + code: async () => { + const component = await webflow.getCurrentComponent() - if (component) { - const props = await component.getProps() - console.log(props) + if (component) { + const props = await component.getProps() + console.log(props) - // Components with no props return an empty array - const blankComponent = await webflow.getComponentByName('Empty Component') - const emptyProps = await blankComponent?.getProps() - console.log(emptyProps) // [] - } else { - console.log('Not currently editing a component.') - } + // Components with no props return an empty array + const blankComponent = await webflow.getComponentByName('Empty Component') + const emptyProps = await blankComponent?.getProps() + console.log(emptyProps) // [] + } else { + console.log('Not currently editing a component.') + } + }, }, - getInstanceProps: async () => { - // Get the selected component instance - const instanceEl = await webflow.getSelectedElement(); + getInstanceProps: { + displayName: 'Get instance props', + code: async () => { + // Get the selected component instance + const instanceEl = await webflow.getSelectedElement(); - if (instanceEl?.type === 'ComponentInstance') { - const props = await instanceEl.getProps(); + if (instanceEl?.type === 'ComponentInstance') { + const props = await instanceEl.getProps(); - // Distinguish bound props from static values using the sourceType property - for (const prop of props) { - if (typeof prop.value === 'object' && prop.value !== null && 'sourceType' in prop.value) { - console.log(`${prop.propId}: bound to ${(prop.value as { sourceType: string }).sourceType}`); - } else { - console.log(`${prop.propId}: ${JSON.stringify(prop.value)}${prop.hasOverride ? ' (overridden)' : ''}`); + // Distinguish bound props from static values using the sourceType property + for (const prop of props) { + if (typeof prop.value === 'object' && prop.value !== null && 'sourceType' in prop.value) { + console.log(`${prop.propId}: bound to ${(prop.value as { sourceType: string }).sourceType}`); + } else { + console.log(`${prop.propId}: ${JSON.stringify(prop.value)}${prop.hasOverride ? ' (overridden)' : ''}`); + } } - } - // Round-trip: read current values, modify one, and write them back with setProps - const updatedProps = props.map((prop) => { - if (prop.propId === props[0].propId) { - return { propId: prop.propId, value: 'Updated value' }; - } - return { propId: prop.propId, value: prop.value }; - }); + // Round-trip: read current values, modify one, and write them back with setProps + const updatedProps = props.map((prop) => { + if (prop.propId === props[0].propId) { + return { propId: prop.propId, value: 'Updated value' }; + } + return { propId: prop.propId, value: prop.value }; + }); - await instanceEl.setProps(updatedProps); - } else { - console.log('Please select a component instance.'); - } + await instanceEl.setProps(updatedProps); + } else { + console.log('Please select a component instance.'); + } + }, }, - getResolvedProps: async () => { - // Get the selected component instance - const instanceEl = await webflow.getSelectedElement(); + getResolvedProps: { + displayName: 'Get resolved props', + code: async () => { + // Get the selected component instance + const instanceEl = await webflow.getSelectedElement(); - if (instanceEl?.type === 'ComponentInstance') { - // Get what each prop actually renders — bindings resolved to their output values - const resolvedProps = await instanceEl.getResolvedProps(); + if (instanceEl?.type === 'ComponentInstance') { + // Get what each prop actually renders — bindings resolved to their output values + const resolvedProps = await instanceEl.getResolvedProps(); - for (const prop of resolvedProps) { - console.log(`${prop.propId}: ${JSON.stringify(prop.value)}`); - } + for (const prop of resolvedProps) { + console.log(`${prop.propId}: ${JSON.stringify(prop.value)}`); + } - // Comparison of all three instance prop read APIs: - - // searchProps — full metadata: wrapped value, resolved value, display info, override status - const search = await instanceEl.searchProps(); - console.log(search[0].value); // { sourceType: 'static', value: 'My Custom Title' } - console.log(search[0].resolvedValue); // 'My Custom Title' - console.log(search[0].display); // { label: 'Heading', group: 'Content' } - console.log(search[0].hasOverride); // true - - // getProps — raw values with override status, no display metadata - const props = await instanceEl.getProps(); - console.log(props[0].value); // 'My Custom Title' (bare value) - console.log(props[0].hasOverride); // true - - // getResolvedProps — just the final resolved output, no binding metadata - const resolved = await instanceEl.getResolvedProps(); - console.log(resolved[0].value); // 'My Custom Title' - } else { - console.log('Please select a component instance.'); - } + // searchProps — full metadata: wrapped value, resolved value, display info, override status + const search = await instanceEl.searchProps(); + console.log(search[0].value); // { sourceType: 'static', value: 'My Custom Title' } + console.log(search[0].resolvedValue); // 'My Custom Title' + console.log(search[0].display); // { label: 'Heading', group: 'Content' } + console.log(search[0].hasOverride); // true + + // getProps — raw values with override status, no display metadata + const props = await instanceEl.getProps(); + console.log(props[0].value); // 'My Custom Title' (bare value) + console.log(props[0].hasOverride); // true + + // getResolvedProps — just the final resolved output, no binding metadata + const resolved = await instanceEl.getResolvedProps(); + console.log(resolved[0].value); // 'My Custom Title' + } else { + console.log('Please select a component instance.'); + } + }, }, - setProps: async () => { - // Get the selected component instance - const instanceEl = await webflow.getSelectedElement(); + setProps: { + displayName: 'Set props', + code: async () => { + // Get the selected component instance + const instanceEl = await webflow.getSelectedElement(); - if (instanceEl?.type === 'ComponentInstance') { - // Read current prop values (round-trip: read, modify, write back) - const currentProps = await instanceEl.getProps(); - console.log('Current props:', currentProps); + if (instanceEl?.type === 'ComponentInstance') { + // Read current prop values (round-trip: read, modify, write back) + const currentProps = await instanceEl.getProps(); + console.log('Current props:', currentProps); - await instanceEl.setProps([ - // Static value override — set the first prop to a new string value - { propId: currentProps[0].propId, value: 'New Heading' }, + await instanceEl.setProps([ + // Static value override — set the first prop to a new string value + { propId: currentProps[0].propId, value: 'New Heading' }, - // Bind to a parent component prop - { propId: 'prop_2', value: { sourceType: 'prop', propId: 'parent_prop_5' } }, + // Bind to a parent component prop + { propId: 'prop_2', value: { sourceType: 'prop', propId: 'parent_prop_5' } }, - // Bind to a CMS field - { propId: 'prop_3', value: { sourceType: 'cms', collectionId: 'col_abc', fieldId: 'field_author' } }, + // Bind to a CMS field + { propId: 'prop_3', value: { sourceType: 'cms', collectionId: 'col_abc', fieldId: 'field_author' } }, - // Bind to a page field - { propId: 'prop_4', value: { sourceType: 'page', fieldKey: 'seoTitle' } }, + // Bind to a page field + { propId: 'prop_4', value: { sourceType: 'page', fieldKey: 'seoTitle' } }, - // Disconnect a binding / reset a prop to its component default - { propId: 'prop_5', value: null }, - ]); + // Disconnect a binding / reset a prop to its component default + { propId: 'prop_5', value: null }, + ]); - // Confirm updated values - const updatedProps = await instanceEl.getProps(); - console.log('Updated props:', updatedProps); - } else { - console.log('Please select a component instance.'); - } + // Confirm updated values + const updatedProps = await instanceEl.getProps(); + console.log('Updated props:', updatedProps); + } else { + console.log('Please select a component instance.'); + } + }, }, - resetAllProps: async () => { - // Get the selected component instance - const instanceEl = await webflow.getSelectedElement(); + resetAllProps: { + displayName: 'Reset all props', + code: async () => { + // Get the selected component instance + const instanceEl = await webflow.getSelectedElement(); - if (instanceEl?.type === 'ComponentInstance') { - // Get the instance's props to find a prop ID to override - const props = await instanceEl.searchProps(); + if (instanceEl?.type === 'ComponentInstance') { + // Get the instance's props to find a prop ID to override + const props = await instanceEl.searchProps(); - if (props.length > 0) { - const firstProp = props[0]; + if (props.length > 0) { + const firstProp = props[0]; - // Set an override on the first prop - await instanceEl.setProps([{ propId: firstProp.propId, value: 'Custom value' }]); + // Set an override on the first prop + await instanceEl.setProps([{ propId: firstProp.propId, value: 'Custom value' }]); - // Confirm the override is applied - const before = await instanceEl.getProps(); - console.log('Has override:', before[0].hasOverride); // true + // Confirm the override is applied + const before = await instanceEl.getProps(); + console.log('Has override:', before[0].hasOverride); // true - // Reset all overrides to component defaults in one call - await instanceEl.resetAllProps(); + // Reset all overrides to component defaults in one call + await instanceEl.resetAllProps(); - // All props now show defaults, no overrides - const after = await instanceEl.getProps(); - console.log('Has override after reset:', after[0].hasOverride); // false + // All props now show defaults, no overrides + const after = await instanceEl.getProps(); + console.log('Has override after reset:', after[0].hasOverride); // false + } else { + console.log('This component instance has no props.'); + } } else { - console.log('This component instance has no props.'); + console.log('Please select a component instance.'); } - } else { - console.log('Please select a component instance.'); - } + }, }, - searchProps: async () => { - // Get the selected element and confirm it's a component instance - const selected = await webflow.getSelectedElement() + searchProps: { + displayName: 'Search props', + code: async () => { + // Get the selected element and confirm it's a component instance + const selected = await webflow.getSelectedElement() - if (selected?.type !== 'ComponentInstance') { - console.log('Select a component instance first.') - return - } - - // Get all props on the instance with their current values and metadata - const props = await selected.searchProps() - console.log(props) - /* - [ - { - propId: "prop_1", - valueType: "textContent", - hasOverride: true, - value: { - sourceType: "static", - value: "My Custom Title" - }, - resolvedValue: "My Custom Title", - defaultValue: "Welcome to our site", - display: { - label: "Heading", - group: "Content" - } - }, - { - propId: "prop_3", - valueType: "textContent", - hasOverride: false, - value: { - sourceType: "cms", - collectionId: "col_abc123", - collectionName: "Blog Posts", - fieldId: "field_author", - fieldName: "Author Name", - fieldGroup: null, - fieldType: "plainText" - }, - resolvedValue: "Jane Doe", - defaultValue: "", - display: { - label: "Author", - group: "Content" - } - }, - { - propId: "prop_4", - valueType: "boolean", - hasOverride: false, - value: { - sourceType: "static", - value: true - }, - resolvedValue: true, - defaultValue: true, - display: { - label: "Show CTA", - group: "Settings", - trueLabel: "Visible", - falseLabel: "Hidden" - } + if (selected?.type !== 'ComponentInstance') { + console.log('Select a component instance first.') + return } - ] - */ + + // Get all props on the instance with their current values and metadata + const props = await selected.searchProps() + console.log(props) + }, }, - searchPropsByValueType: async () => { - // Get the selected element and confirm it's a component instance - const selected = await webflow.getSelectedElement() + searchPropsByValueType: { + displayName: 'Search props by value type', + code: async () => { + // Get the selected element and confirm it's a component instance + const selected = await webflow.getSelectedElement() - if (selected?.type !== 'ComponentInstance') { - console.log('Select a component instance first.') - return - } + if (selected?.type !== 'ComponentInstance') { + console.log('Select a component instance first.') + return + } - // Filter to only props with a specific value type - const textProps = await selected.searchProps({ valueType: 'textContent' }) - console.log(textProps) - // Returns only props where valueType === 'textContent' + // Filter to only props with a specific value type + const textProps = await selected.searchProps({ valueType: 'textContent' }) + console.log(textProps) + // Returns only props where valueType === 'textContent' + }, }, }, } diff --git a/src/examples/elements.ts b/src/examples/elements.ts index 07bb9b3..588f9b5 100644 --- a/src/examples/elements.ts +++ b/src/examples/elements.ts @@ -12,189 +12,263 @@ export enum LinkModeSettings { export const Elements = { // Element Management elementManagement: { - getSelectedElement: async () => { - // Get Selected Element - const element = await webflow.getSelectedElement() - - // Print element info - if (element) { - console.log(element) - console.log(`Element type: ${element.type}`) - } else { - console.log('No element is currently selected.') - } + getSelectedElement: { + displayName: 'Get selected element', + code: async () => { + // Get Selected Element + const element = await webflow.getSelectedElement() + + // Print element info + if (element) { + console.log(element) + console.log(`Element type: ${element.type}`) + } else { + console.log('No element is currently selected.') + } + }, }, - setSelectedElement: async () => { - // Get the Root Element - const rootElement = await webflow.getRootElement() + setSelectedElement: { + displayName: 'Set selected element', + code: async () => { + // Get the Root Element + const rootElement = await webflow.getRootElement() - if (rootElement) { - // Select the root element - const selectedElement = await webflow.setSelectedElement(rootElement) + if (rootElement) { + // Select the root element + const selectedElement = await webflow.setSelectedElement(rootElement) - if (selectedElement?.children) { - // Start building elements on the selected element - await selectedElement?.append(webflow.elementPresets.DOM) + if (selectedElement?.children) { + // Start building elements on the selected element + await selectedElement?.append(webflow.elementPresets.DOM) + } } - } + }, + }, + + getAllElements: { + displayName: 'Get all elements', + code: async () => { + // Retrieve all elements in the current context + const allElements = await webflow.getAllElements() + + // Print element list + if (allElements.length > 0) { + console.log('List of all elements:') + + allElements.forEach((element, index) => { + console.log( + index + 1, + 'Element ID:', + JSON.stringify(element), + 'Element Type:', + element.type, + ) + }) + } else { + console.log('No elements found in the current context.') + } + }, }, - getAllElements: async () => { - // Retrieve all elements in the current context - const allElements = await webflow.getAllElements() + getRootElement: { + displayName: 'Get root element', + code: async () => { + // Get Root Element + const rootElement = await webflow.getRootElement() + + // Print element details + console.log( + `Type: ${rootElement?.type} \n ID: ${JSON.stringify(rootElement?.id)}`, + ) + }, + }, - // Print element list - if (allElements.length > 0) { - console.log('List of all elements:') + removeElement: { + displayName: 'Remove element', + code: async () => { + // Get Selected Element + const el = await webflow.getSelectedElement() - allElements.forEach((element, index) => { - console.log( - index + 1, - 'Element ID:', - JSON.stringify(element), - 'Element Type:', - element.type, - ) - }) - } else { - console.log('No elements found in the current context.') - } + // Remove the selected element + await el?.remove() + }, }, - getRootElement: async () => { - // Get Root Element - const rootElement = await webflow.getRootElement() + getParentComponent: { + displayName: 'Get parent component', + code: async () => { + // Get Selected Element + const element = await webflow.getSelectedElement() - // Print element details - console.log( - `Type: ${rootElement?.type} \n ID: ${JSON.stringify(rootElement?.id)}`, - ) + if (element) { + // Get the component that directly contains the element + const parentComponent = await element.getParentComponent() + + if (parentComponent) { + const name = await parentComponent.getName() + console.log(`Element is inside component: ${name}`) + } else { + console.log('Element is not inside a component.') + } + } else { + console.log('No element is currently selected.') + } + }, }, - removeElement: async () => { - // Get Selected Element - const el = await webflow.getSelectedElement() + getTag: { + displayName: 'Get tag', + code: async () => { + // Get the HTML tag of the selected element + const el = await webflow.getSelectedElement(); - // Remove the selected element - await el?.remove() + if (el?.type === 'Block') { + const tag = await el.getTag(); // BlockElementTag | null + console.log(tag); // e.g. 'section' + } + + if (el?.type === 'Heading') { + const tag = await el.getTag(); // HeadingTag | null + console.log(tag); // e.g. 'h2' + } + + if (el?.type === 'List') { + const tag = await el.getTag(); // ListTag | null + console.log(tag); // 'ul' or 'ol' + } + }, }, - getParentComponent: async () => { - // Get Selected Element - const element = await webflow.getSelectedElement() + setTag: { + displayName: 'Set tag', + code: async (myTag: string) => { + // Set the HTML tag of the selected element + const element = await webflow.getSelectedElement(); - if (element) { - // Get the component that directly contains the element - const parentComponent = await element.getParentComponent() + // Change a Section element to use a nav tag + if (element?.type === 'Section') { + await element.setTag('nav'); + } - if (parentComponent) { - const name = await parentComponent.getName() - console.log(`Element is inside component: ${name}`) - } else { - console.log('Element is not inside a component.') - } - } else { - console.log('No element is currently selected.') - } - }, - - getTag: async () => { - // Get the HTML tag of the selected element - const el = await webflow.getSelectedElement(); - - if (el?.type === 'Block') { - const tag = await el.getTag(); // BlockElementTag | null - console.log(tag); // e.g. 'section' - } - - if (el?.type === 'Heading') { - const tag = await el.getTag(); // HeadingTag | null - console.log(tag); // e.g. 'h2' - } - - if (el?.type === 'List') { - const tag = await el.getTag(); // ListTag | null - console.log(tag); // 'ul' or 'ol' - } - }, - - setTag: async (myTag: string) => { - // Set the HTML tag of the selected element - const element = await webflow.getSelectedElement(); - - // Change a Section element to use a nav tag - if (element?.type === 'Section') { - await element.setTag('nav'); - } - - // Change a Heading element from h1 to h2 and change its CMS binding - if (element?.type === 'Heading') { - await element.setTag('h2'); - // Bind to a CMS field - await element.setTag({ - sourceType: 'cms', - collectionId: 'col_abc', - fieldId: 'field_xyz', - }); - } - - // Change a List element from unordered to ordered - if (element?.type === 'List') { - await element.setTag('ol'); - } + // Change a Heading element from h1 to h2 and change its CMS binding + if (element?.type === 'Heading') { + await element.setTag('h2'); + // Bind to a CMS field + await element.setTag({ + sourceType: 'cms', + collectionId: 'col_abc', + fieldId: 'field_xyz', + }); + } + + // Change a List element from unordered to ordered + if (element?.type === 'List') { + await element.setTag('ol'); + } + }, }, }, // Element Creation elementCreation: { - BulkAddElements: async () => { - // Get the selected element as the container - const selectedElement = await webflow.getSelectedElement() - - // Create a nav container - const navMenu = webflow.elementBuilder(webflow.elementPresets.DOM) - navMenu.setTag('nav') - - // Menu items to add - const menuItems = ['Home', 'About', 'Services', 'Portfolio', 'Contact'] - - // Create all menu items at once and store references for later - const menuItemRefs = [] - menuItems.forEach((itemText) => { - const item = navMenu.append(webflow.elementPresets.DOM) - item.setTag('a') - item.setAttribute('href', '#') - // Store reference to set text later - menuItemRefs.push(item) - }) - - // Add the entire menu to the canvas in one operation - if (selectedElement?.children) { - await selectedElement.append(navMenu) - console.log( - 'Navigation structure with 5 items created in one operation', - ) - - // Text content must be set after elements are added to the canvas - const elements = await webflow.getAllElements() + BulkAddElements: { + displayName: 'Bulk add elements', + code: async () => { + // Get the selected element as the container + const selectedElement = await webflow.getSelectedElement() + + // Create a nav container + const navMenu = webflow.elementBuilder(webflow.elementPresets.DOM) + navMenu.setTag('nav') + + // Menu items to add + const menuItems = ['Home', 'About', 'Services', 'Portfolio', 'Contact'] + + // Create all menu items at once and store references for later + const menuItemRefs = [] + menuItems.forEach((itemText) => { + const item = navMenu.append(webflow.elementPresets.DOM) + item.setTag('a') + item.setAttribute('href', '#') + // Store reference to set text later + menuItemRefs.push(item) + }) - // Set text content for each menu item - for (let i = 0; i < menuItemRefs.length; i++) { - const menuItemElement = elements.find( - (el) => el.id.element === menuItemRefs[i].id, + // Add the entire menu to the canvas in one operation + if (selectedElement?.children) { + await selectedElement.append(navMenu) + console.log( + 'Navigation structure with 5 items created in one operation', ) - if (menuItemElement) { - await menuItemElement.setTextContent(menuItems[i]) + + // Text content must be set after elements are added to the canvas + const elements = await webflow.getAllElements() + + // Set text content for each menu item + for (let i = 0; i < menuItemRefs.length; i++) { + const menuItemElement = elements.find( + (el) => el.id.element === menuItemRefs[i].id, + ) + if (menuItemElement) { + await menuItemElement.setTextContent(menuItems[i]) + } + } + + // Example of applying styles after elements are on the canvas + // First, create the styles + const navStyle = await webflow.createStyle('navContainer') + await navStyle.setProperties({ + display: 'flex', + 'row-gap': '20px', + 'padding-left': '15px', + 'padding-right': '15px', + 'padding-top': '15px', + 'padding-bottom': '15px', + 'background-color': '#f5f5f5', + 'border-radius': '8px', + }) + + const navItemStyle = await webflow.createStyle('navItem') + await navItemStyle.setProperties({ + color: '#333', + 'text-decoration': 'none', + 'padding-left': '12px', + 'padding-right': '12px', + 'padding-top': '8px', + 'padding-bottom': '8px', + 'border-radius': '4px', + 'font-weight': '500', + }) + + // Then find and apply styles to the elements + const navElement = elements.find((el) => el.id.element === navMenu.id) + if (navElement) { + await navElement.setStyles([navStyle]) + + // Apply styles to all menu items + for (const menuItemRef of menuItemRefs) { + const menuItem = elements.find( + (el) => el.id.element === menuItemRef.id, + ) + if (menuItem) { + await menuItem.setStyles([navItemStyle]) + } + } } } + }, + }, - // Style application would also come after the elements are added - // (Styling example shown below for completeness) + BulkAddElementsNavMenu: { + displayName: 'Bulk add elements nav menu', + code: async () => { + // Start by creating some styles that will be applied to the nav container. - // Example of applying styles after elements are on the canvas - // First, create the styles - const navStyle = await webflow.createStyle('navContainer') + let navStyle = await webflow.getStyleByName('navContainer') + if (!navStyle) { + navStyle = await webflow.createStyle('navContainer') + } await navStyle.setProperties({ display: 'flex', 'row-gap': '20px', @@ -206,7 +280,10 @@ export const Elements = { 'border-radius': '8px', }) - const navItemStyle = await webflow.createStyle('navItem') + let navItemStyle = await webflow.getStyleByName('navItem') + if (!navItemStyle) { + navItemStyle = await webflow.createStyle('navItem') + } await navItemStyle.setProperties({ color: '#333', 'text-decoration': 'none', @@ -218,1120 +295,1242 @@ export const Elements = { 'font-weight': '500', }) - // Then find and apply styles to the elements - const navElement = elements.find((el) => el.id.element === navMenu.id) - if (navElement) { - await navElement.setStyles([navStyle]) + // Get the selected element as the container + const selectedElement = await webflow.getSelectedElement() + + // Create a nav container + const navMenu = webflow.elementBuilder(webflow.elementPresets.DOM) + navMenu.setTag('nav') + navMenu.setStyles([navStyle]) + + // Menu items to add + const menuItems = ['Home', 'About', 'Services', 'Portfolio', 'Contact'] + + // Create all menu items at once and store references for later + const menuItemRefs = [] + menuItems.forEach((itemText) => { + const item = navMenu.append(webflow.elementPresets.DOM) + item.setTag('a') + item.setAttribute('href', '#') + item.setTextContent(itemText) + item.setStyles([navItemStyle]) + // Store reference to set text later + menuItemRefs.push(item) + }) - // Apply styles to all menu items - for (const menuItemRef of menuItemRefs) { - const menuItem = elements.find( - (el) => el.id.element === menuItemRef.id, - ) - if (menuItem) { - await menuItem.setStyles([navItemStyle]) - } - } + // Add the entire menu to the canvas in one operation + if (selectedElement?.children) { + await selectedElement.append(navMenu) + console.log( + 'Navigation structure with 5 items created in one operation', + ) } - } - }, - - BulkAddElementsNavMenu: async () => { - // Start by creating some styles that will be applied to the nav container. - - let navStyle = await webflow.getStyleByName('navContainer') - if (!navStyle) { - navStyle = await webflow.createStyle('navContainer') - } - await navStyle.setProperties({ - display: 'flex', - 'row-gap': '20px', - 'padding-left': '15px', - 'padding-right': '15px', - 'padding-top': '15px', - 'padding-bottom': '15px', - 'background-color': '#f5f5f5', - 'border-radius': '8px', - }) - - let navItemStyle = await webflow.getStyleByName('navItem') - if (!navItemStyle) { - navItemStyle = await webflow.createStyle('navItem') - } - await navItemStyle.setProperties({ - color: '#333', - 'text-decoration': 'none', - 'padding-left': '12px', - 'padding-right': '12px', - 'padding-top': '8px', - 'padding-bottom': '8px', - 'border-radius': '4px', - 'font-weight': '500', - }) - - // Get the selected element as the container - const selectedElement = await webflow.getSelectedElement() - - // Create a nav container - const navMenu = webflow.elementBuilder(webflow.elementPresets.DOM) - navMenu.setTag('nav') - navMenu.setStyles([navStyle]) - - // Menu items to add - const menuItems = ['Home', 'About', 'Services', 'Portfolio', 'Contact'] - - // Create all menu items at once and store references for later - const menuItemRefs = [] - menuItems.forEach((itemText) => { - const item = navMenu.append(webflow.elementPresets.DOM) - item.setTag('a') - item.setAttribute('href', '#') - item.setTextContent(itemText) - item.setStyles([navItemStyle]) - // Store reference to set text later - menuItemRefs.push(item) - }) - - // Add the entire menu to the canvas in one operation - if (selectedElement?.children) { - await selectedElement.append(navMenu) - console.log( - 'Navigation structure with 5 items created in one operation', + }, + }, + + BulkAddElementSVG: { + displayName: 'Bulk add element SVG', + code: async () => { + // Get the selected element as the container + const selectedElement = await webflow.getSelectedElement() + + // Create an SVG builder element + const svgBuilder = webflow.elementBuilder(webflow.elementPresets.DOM) + svgBuilder.setTag('svg') + svgBuilder.setAttribute('viewBox', '0 0 100 100') + svgBuilder.setAttribute('xmlns', 'http://www.w3.org/2000/svg') + svgBuilder.setAttribute('width', '200') + svgBuilder.setAttribute('height', '200') + + // Create rainbow circular background with multiple circles + const backgroundElements = [] + const rainbowColors = [ + 'hsl(0, 90%, 55%)', // Red + 'hsl(30, 90%, 55%)', // Orange + 'hsl(60, 90%, 55%)', // Yellow + 'hsl(120, 90%, 55%)', // Green + 'hsl(240, 90%, 55%)', // Blue + 'hsl(270, 90%, 55%)', // Indigo + 'hsl(300, 90%, 55%)', // Violet + ] + + for (let i = 0; i < 7; i++) { + const circle = svgBuilder.append(webflow.elementPresets.DOM) + circle.setTag('circle') + circle.setAttribute('cx', '50') + circle.setAttribute('cy', '50') + circle.setAttribute('r', `${46 - i * 3}`) + circle.setAttribute('fill', 'none') + circle.setAttribute('stroke', rainbowColors[i]) + circle.setAttribute('stroke-width', '2.5') + circle.setAttribute('opacity', '0.9') + backgroundElements.push(circle) + } + + // Create the central background circle + const centralCircle = svgBuilder.append(webflow.elementPresets.DOM) + centralCircle.setTag('circle') + centralCircle.setAttribute('cx', '50') + centralCircle.setAttribute('cy', '50') + centralCircle.setAttribute('r', '32') + centralCircle.setAttribute('fill', 'white') + + // Create the "Webflow" logo + const logoPath = svgBuilder.append(webflow.elementPresets.DOM) + logoPath.setTag('path') + logoPath.setAttribute( + 'd', + 'M61.3811 14L43.0716 49.7933H25.8737L33.5362 34.959H33.1924C26.8709 43.1653 17.439 48.5674 4 49.7933V35.1643C4 35.1643 12.5972 34.6565 17.6513 29.3429H4V14.0003H19.3426V26.6194L19.687 26.6179L25.9565 14.0003H37.5597V26.5393L37.9041 26.5388L44.4089 14H61.3811Z', ) - } - }, - - BulkAddElementSVG: async () => { - // Get the selected element as the container - const selectedElement = await webflow.getSelectedElement() - - // Create an SVG builder element - const svgBuilder = webflow.elementBuilder(webflow.elementPresets.DOM) - svgBuilder.setTag('svg') - svgBuilder.setAttribute('viewBox', '0 0 100 100') - svgBuilder.setAttribute('xmlns', 'http://www.w3.org/2000/svg') - svgBuilder.setAttribute('width', '200') - svgBuilder.setAttribute('height', '200') - - // Create rainbow circular background with multiple circles - const backgroundElements = [] - const rainbowColors = [ - 'hsl(0, 90%, 55%)', // Red - 'hsl(30, 90%, 55%)', // Orange - 'hsl(60, 90%, 55%)', // Yellow - 'hsl(120, 90%, 55%)', // Green - 'hsl(240, 90%, 55%)', // Blue - 'hsl(270, 90%, 55%)', // Indigo - 'hsl(300, 90%, 55%)', // Violet - ] - - for (let i = 0; i < 7; i++) { - const circle = svgBuilder.append(webflow.elementPresets.DOM) - circle.setTag('circle') - circle.setAttribute('cx', '50') - circle.setAttribute('cy', '50') - circle.setAttribute('r', `${46 - i * 3}`) - circle.setAttribute('fill', 'none') - circle.setAttribute('stroke', rainbowColors[i]) - circle.setAttribute('stroke-width', '2.5') - circle.setAttribute('opacity', '0.9') - backgroundElements.push(circle) - } - - // Create the central background circle - const centralCircle = svgBuilder.append(webflow.elementPresets.DOM) - centralCircle.setTag('circle') - centralCircle.setAttribute('cx', '50') - centralCircle.setAttribute('cy', '50') - centralCircle.setAttribute('r', '32') - centralCircle.setAttribute('fill', 'white') - - // Create the "Webflow" logo - const logoPath = svgBuilder.append(webflow.elementPresets.DOM) - logoPath.setTag('path') - logoPath.setAttribute( - 'd', - 'M61.3811 14L43.0716 49.7933H25.8737L33.5362 34.959H33.1924C26.8709 43.1653 17.439 48.5674 4 49.7933V35.1643C4 35.1643 12.5972 34.6565 17.6513 29.3429H4V14.0003H19.3426V26.6194L19.687 26.6179L25.9565 14.0003H37.5597V26.5393L37.9041 26.5388L44.4089 14H61.3811Z', - ) - logoPath.setAttribute('fill', '#146EF5') - logoPath.setAttribute('fill-rule', 'evenodd') - logoPath.setAttribute('clip-rule', 'evenodd') - logoPath.setAttribute('transform', 'translate(30.5, 30.5) scale(0.7)') - - // Add the entire SVG structure to the canvas in one operation - if (selectedElement?.children) { - await selectedElement.append(svgBuilder) - console.log('webflow logo created successfully') - } - }, - - insertElementBefore: async () => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - if (selectedElement) { - // Insert a heading before the selected element with initial settings - const newHeading = await selectedElement.before(webflow.elementPresets.Heading, { - tag: 'h2', - domId: 'section-title', - }) + logoPath.setAttribute('fill', '#146EF5') + logoPath.setAttribute('fill-rule', 'evenodd') + logoPath.setAttribute('clip-rule', 'evenodd') + logoPath.setAttribute('transform', 'translate(30.5, 30.5) scale(0.7)') - // Print element details - console.log(JSON.stringify(newHeading)) - } + // Add the entire SVG structure to the canvas in one operation + if (selectedElement?.children) { + await selectedElement.append(svgBuilder) + console.log('webflow logo created successfully') + } + }, }, - insertElementAfter: async () => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() + insertElementBefore: { + displayName: 'Insert element before', + code: async () => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() - if (selectedElement) { - // Insert a heading after the selected element with initial settings - const newHeading = await selectedElement.after(webflow.elementPresets.Heading, { - tag: 'h2', - domId: 'section-title', - }) + if (selectedElement) { + // Insert a heading before the selected element with initial settings + const newHeading = await selectedElement.before(webflow.elementPresets.Heading, { + tag: 'h2', + domId: 'section-title', + }) - // Print element details - console.log(JSON.stringify(newHeading)) - } + // Print element details + console.log(JSON.stringify(newHeading)) + } + }, }, - appendElement: async () => { - // Get Selected Element - const el = await webflow.getSelectedElement() + insertElementAfter: { + displayName: 'Insert element after', + code: async () => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() - // Check if element supports child elements - if (el?.children) { - // Append a heading element with initial settings applied at creation time - const newHeading = await el.append(webflow.elementPresets.Heading, { - tag: 'h2', - domId: 'section-title', - }) + if (selectedElement) { + // Insert a heading after the selected element with initial settings + const newHeading = await selectedElement.after(webflow.elementPresets.Heading, { + tag: 'h2', + domId: 'section-title', + }) - // Print element details - console.log(JSON.stringify(newHeading)) - } + // Print element details + console.log(JSON.stringify(newHeading)) + } + }, }, - prependElement: async () => { - // Get Selected Element - const el = await webflow.getSelectedElement() + appendElement: { + displayName: 'Append element', + code: async () => { + // Get Selected Element + const el = await webflow.getSelectedElement() - // Check if element supports child elements - if (el?.children) { - // Prepend a heading element with initial settings applied at creation time - const newHeading = await el.prepend(webflow.elementPresets.Heading, { - tag: 'h2', - domId: 'section-title', - }) + // Check if element supports child elements + if (el?.children) { + // Append a heading element with initial settings applied at creation time + const newHeading = await el.append(webflow.elementPresets.Heading, { + tag: 'h2', + domId: 'section-title', + }) - // Print element details - console.log(JSON.stringify(newHeading)) - } + // Print element details + console.log(JSON.stringify(newHeading)) + } + }, + }, + + prependElement: { + displayName: 'Prepend element', + code: async () => { + // Get Selected Element + const el = await webflow.getSelectedElement() + + // Check if element supports child elements + if (el?.children) { + // Prepend a heading element with initial settings applied at creation time + const newHeading = await el.prepend(webflow.elementPresets.Heading, { + tag: 'h2', + domId: 'section-title', + }) + + // Print element details + console.log(JSON.stringify(newHeading)) + } + }, }, }, + // Attributes attributes: { - getAttributes: async () => { - const element = await webflow.getSelectedElement() - - if (element && element.attributes) { - const attributes = await element.getAttributes() - console.log(attributes) - // [{ name: 'data-label', value: 'primary' }] - } + getAttributes: { + displayName: 'Get attributes', + code: async () => { + const element = await webflow.getSelectedElement() + + if (element && element.attributes) { + const attributes = await element.getAttributes() + console.log(attributes) + // [{ name: 'data-label', value: 'primary' }] + } + }, }, - getAttributeValue: async () => { - const element = await webflow.getSelectedElement() + getAttributeValue: { + displayName: 'Get attribute value', + code: async () => { + const element = await webflow.getSelectedElement() - if (element && element.attributes) { - // Look up by name - const value = await element.getAttributeValue('data-label') - console.log(value) // 'primary' + if (element && element.attributes) { + // Look up by name + const value = await element.getAttributeValue('data-label') + console.log(value) // 'primary' - // Look up by index - const first = await element.getAttributeValue(0) - console.log(first) // 'primary' - } + // Look up by index + const first = await element.getAttributeValue(0) + console.log(first) // 'primary' + } + }, }, - getResolvedAttributes: async () => { - const element = await webflow.getSelectedElement() + getResolvedAttributes: { + displayName: 'Get resolved attributes', + code: async () => { + const element = await webflow.getSelectedElement() - if (element && element.attributes) { - const attributes = await element.getResolvedAttributes() - console.log(attributes) - // [{ name: 'data-label', value: 'primary' }] - } + if (element && element.attributes) { + const attributes = await element.getResolvedAttributes() + console.log(attributes) + // [{ name: 'data-label', value: 'primary' }] + } + }, }, - getResolvedAttributeValue: async () => { - const element = await webflow.getSelectedElement() + getResolvedAttributeValue: { + displayName: 'Get resolved attribute value', + code: async ()=> { + const element = await webflow.getSelectedElement() - if (element && element.attributes) { - const value = await element.getResolvedAttributeValue('data-title') - console.log(value) // 'My heading' - } + if (element && element.attributes) { + const value = await element.getResolvedAttributeValue('data-title') + console.log(value) // 'My heading' + } + }, }, - setAttribute: async () => { - const element = await webflow.getSelectedElement() + setAttribute: { + displayName: 'Set attribute', + code: async () => { + const element = await webflow.getSelectedElement() - if (element && element.attributes) { - // Set by name (string values only) - await element.setAttribute('data-label', 'primary') + if (element && element.attributes) { + // Set by name (string values only) + await element.setAttribute('data-label', 'primary') - // Set by index (supports bindings) - await element.setAttribute(0, { name: 'data-label', value: 'primary' }) - } + // Set by index (supports bindings) + await element.setAttribute(0, { name: 'data-label', value: 'primary' }) + } + }, }, - setAttributes: async () => { - const element = await webflow.getSelectedElement() + setAttributes: { + displayName: 'Set attributes', + code: async () => { + const element = await webflow.getSelectedElement() - if (element && element.attributes) { - await element.setAttributes([ - { name: 'data-label', value: 'primary' }, - { name: 'data-index', value: '1' }, - ]) - console.log(await element.getAttributes()) - } + if (element && element.attributes) { + await element.setAttributes([ + { name: 'data-label', value: 'primary' }, + { name: 'data-index', value: '1' }, + ]) + console.log(await element.getAttributes()) + } + }, }, - removeAttribute: async () => { - const element = await webflow.getSelectedElement() + removeAttribute: { + displayName: 'Remove attribute', + code: async () => { + const element = await webflow.getSelectedElement() - if (element && element.attributes) { - // Remove by name - await element.removeAttribute('data-label') + if (element && element.attributes) { + // Remove by name + await element.removeAttribute('data-label') - // Remove by index - await element.removeAttribute(0) - } + // Remove by index + await element.removeAttribute(0) + } + }, }, }, // Text Content textContent: { - getTextContent: async () => { - const selectedElement = await webflow.getSelectedElement() - - // Check if element is a String type - if (selectedElement?.type === 'String') { - const text = await selectedElement.getText() - console.log(text) - } - // Otherwise, check if element has child String elements - else if (selectedElement?.children) { - const children = await selectedElement.getChildren() - const stringElements = children.filter( - (child) => child.type === 'String', - ) + getTextContent: { + displayName: 'Get text content', + code: async () => { + const selectedElement = await webflow.getSelectedElement() + + // Check if element is a String type + if (selectedElement?.type === 'String') { + const text = await selectedElement.getText() + console.log(text) + } + // Otherwise, check if element has child String elements + else if (selectedElement?.children) { + const children = await selectedElement.getChildren() + const stringElements = children.filter( + (child) => child.type === 'String', + ) - if (stringElements.length > 0) { - const textContentPromises = stringElements.map( - async (stringElement: StringElement) => { - const text = await stringElement.getText() - return text - }, + if (stringElements.length > 0) { + const textContentPromises = stringElements.map( + async (stringElement: StringElement) => { + const text = await stringElement.getText() + return text + }, + ) + const textContent = await Promise.all(textContentPromises) + console.log(textContent) + } else { + console.log('Element does not support text content') + } + } + }, + }, + + setTextContent: { + displayName: 'Set text content', + code: async (myText: string) => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() + + // Check if element supports text content + if (selectedElement?.textContent) { + // Set and print text content + const text = await selectedElement.setTextContent(myText) + console.log(selectedElement.textContent) + + // Check if element has child String elements + } else if (selectedElement?.children) { + const children = await selectedElement.getChildren() + const stringElements = children.filter( + (child) => child.type === 'String', ) - const textContent = await Promise.all(textContentPromises) - console.log(textContent) + if (stringElements.length > 0) { + // Set text content on child String elements + const text = await stringElements[0].setText(myText) + console.log(text) + } } else { console.log('Element does not support text content') } - } - }, - - setTextContent: async (myText: string) => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - // Check if element supports text content - if (selectedElement?.textContent) { - // Set and print text content - const text = await selectedElement.setTextContent(myText) - console.log(selectedElement.textContent) - - // Check if element has child String elements - } else if (selectedElement?.children) { - const children = await selectedElement.getChildren() - const stringElements = children.filter( - (child) => child.type === 'String', - ) - if (stringElements.length > 0) { - // Set text content on child String elements - const text = await stringElements[0].setText(myText) - console.log(text) - } - } else { - console.log('Element does not support text content') - } + }, }, - getText: async () => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() + getText: { + displayName: 'Get text', + code: async () => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() - if (selectedElement?.textContent && selectedElement?.children) { - // Get Child Elements - const children = await selectedElement.getChildren() + if (selectedElement?.textContent && selectedElement?.children) { + // Get Child Elements + const children = await selectedElement.getChildren() - // Filter string elements from children - const strings = children.filter((child) => child.type === 'String') + // Filter string elements from children + const strings = children.filter((child) => child.type === 'String') - // Initialize an array to hold text content - let textContent = [] + // Initialize an array to hold text content + let textContent = [] - // Loop over string elements to get text - for (const myString of strings) { - if (myString.type === 'String') { - const text = await myString.getText() - textContent.push(text) + // Loop over string elements to get text + for (const myString of strings) { + if (myString.type === 'String') { + const text = await myString.getText() + textContent.push(text) + } } - } - // Print text - console.log(textContent) - } + // Print text + console.log(textContent) + } + }, }, - setText: async (myText: string) => { - // Get all elements and find the first StringElement - const allElements = await webflow.getAllElements() - const foundElement = allElements.find((el) => el.type === 'String') + setText: { + displayName: 'Set text', + code: async (myText: string) => { + // Get all elements and find the first StringElement + const allElements = await webflow.getAllElements() + const foundElement = allElements.find((el) => el.type === 'String') - if (foundElement) { - // Check that element has the method in order to use it - if ('setText' in foundElement) { - const elementText = foundElement.setText(myText) // Set Text + if (foundElement) { + // Check that element has the method in order to use it + if ('setText' in foundElement) { + const elementText = foundElement.setText(myText) // Set Text + } + } else { + console.log('Element not found on page') } - } else { - console.log('Element not found on page') - } + }, }, }, // Styles styles: { - getStyles: async () => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - if (selectedElement?.styles) { - // Get Styles - const styles = await selectedElement.getStyles() - - // Get Style Details - const styleDetails = styles.map(async (style) => { - const styleName = await style.getName() - const styleProperties = await style.getProperties() - - return { - Name: styleName, - Properties: styleProperties, - ID: style.id, - } - }) + getStyles: { + displayName: 'Get styles', + code: async () => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() + + if (selectedElement?.styles) { + // Get Styles + const styles = await selectedElement.getStyles() - // Print Style Details - console.log(await Promise.all(styleDetails)) - } + // Get Style Details + const styleDetails = styles.map(async (style) => { + const styleName = await style.getName() + const styleProperties = await style.getProperties() + + return { + Name: styleName, + Properties: styleProperties, + ID: style.id, + } + }) + + // Print Style Details + console.log(await Promise.all(styleDetails)) + } + }, }, - setStyles: async (styleName: string) => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() + setStyles: { + displayName: 'Set styles', + code: async (styleName: string) => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() - if (selectedElement?.styles) { - // Get Style by name - const myStyle = await webflow.getStyleByName(styleName) + if (selectedElement?.styles) { + // Get Style by name + const myStyle = await webflow.getStyleByName(styleName) - if (myStyle) { - // Set and print style - await selectedElement.setStyles([myStyle]) - const styles = await selectedElement.getStyles() - console.log(`Styles: ${JSON.stringify(styles)}`) + if (myStyle) { + // Set and print style + await selectedElement.setStyles([myStyle]) + const styles = await selectedElement.getStyles() + console.log(`Styles: ${JSON.stringify(styles)}`) + } } - } + }, }, - createAndSetStyle: async () => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() + createAndSetStyle: { + displayName: 'Create and set style', + code: async () => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() - if (selectedElement?.styles) { - // Create a new style - const newStyle = await webflow.createStyle('MyCustomStyle') + if (selectedElement?.styles) { + // Create a new style + const newStyle = await webflow.createStyle('MyCustomStyle') - // Set properties for the style - newStyle.setProperties({ - 'background-color': 'blue', - 'font-size': '32px', - 'font-weight': 'bold', - }) + // Set properties for the style + newStyle.setProperties({ + 'background-color': 'blue', + 'font-size': '32px', + 'font-weight': 'bold', + }) - // Set style on selected element - selectedElement.setStyles([newStyle]) - } + // Set style on selected element + selectedElement.setStyles([newStyle]) + } + }, }, }, // DOM Operations domOperations: { + getAllAttributes: { + displayName: 'Get all attributes', + code: async () => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() + + if (selectedElement?.type === 'DOM') { + const customAttributes = await selectedElement.getAllAttributes() + console.log(customAttributes) + } + }, + }, + + getAttribute: { + displayName: 'Get attribute', + code: async (name: string) => { + // Get All Elements and find first DOM Element + const elements = await webflow.getAllElements() + const DOMElement = elements.find((element) => element.type === 'DOM') + + if (DOMElement?.type === 'DOM') { + // Get DOM Element's Attribute by Name + const attribute = await DOMElement.getAttribute(name) + console.log(attribute) + } else { + console.log('No DOM Element Found') + } + }, + }, + + setAttribute: { + displayName: 'Set attribute', + code: async (name: string, value: string) => { + // Get All Elements and find first DOM Element + const elements = await webflow.getAllElements() + const DOMElement = elements.find((element) => element.type === 'DOM') + + if (DOMElement?.type === 'DOM') { + // Set and print DOM Element's Attributes + await DOMElement.setAttribute(name, value) + const attributes = await DOMElement.getAllAttributes() + console.log(attributes) + } else { + console.log('No DOM Element Found') + } + }, + }, + + removeAttribute: { + displayName: 'Remove attribute', + code: async (name: string) => { + // Get All Elements and find first DOM Element + const elements = await webflow.getAllElements() + const DOMElement = elements.find((element) => element.type === 'DOM') + + if (DOMElement?.type === 'DOM') { + // Get Attributes + const customAttributes = await DOMElement.getAllAttributes() + console.log(customAttributes) - getAllAttributes: async () => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - if (selectedElement?.type === 'DOM') { - const customAttributes = await selectedElement.getAllAttributes() - console.log(customAttributes) - } - }, - - getAttribute: async (name: string) => { - // Get All Elements and find first DOM Element - const elements = await webflow.getAllElements() - const DOMElement = elements.find((element) => element.type === 'DOM') - - if (DOMElement?.type === 'DOM') { - // Get DOM Element's Attribute by Name - const attribute = await DOMElement.getAttribute(name) - console.log(attribute) - } else { - console.log('No DOM Element Found') - } - }, - - setAttribute: async (name: string, value: string) => { - // Get All Elements and find first DOM Element - const elements = await webflow.getAllElements() - const DOMElement = elements.find((element) => element.type === 'DOM') - - if (DOMElement?.type === 'DOM') { - // Set and print DOM Element's Attributes - await DOMElement.setAttribute(name, value) - const attributes = await DOMElement.getAllAttributes() - console.log(attributes) - } else { - console.log('No DOM Element Found') - } - }, - - removeAttribute: async (name: string) => { - // Get All Elements and find first DOM Element - const elements = await webflow.getAllElements() - const DOMElement = elements.find((element) => element.type === 'DOM') - - if (DOMElement?.type === 'DOM') { - // Get Attributes - const customAttributes = await DOMElement.getAllAttributes() - console.log(customAttributes) - - // Remove and print DOM Element's Attributes - await DOMElement.removeAttribute(name) - const attributes = await DOMElement.getAllAttributes() - console.log(attributes) - } else { - console.log('No DOM Element Found') - } + // Remove and print DOM Element's Attributes + await DOMElement.removeAttribute(name) + const attributes = await DOMElement.getAllAttributes() + console.log(attributes) + } else { + console.log('No DOM Element Found') + } + }, }, }, // Heading Operations headingOperations: { - getHeadingLevel: async () => { - const selectedElement = await webflow.getSelectedElement() - - if (selectedElement?.type === 'Heading') { - const headingLevel = await selectedElement.getHeadingLevel() - console.log(headingLevel) - } else { - console.log('Selected Element is not a Heading Element') - } + getHeadingLevel: { + displayName: 'Get heading level', + code: async () => { + const selectedElement = await webflow.getSelectedElement() + + if (selectedElement?.type === 'Heading') { + const headingLevel = await selectedElement.getHeadingLevel() + console.log(headingLevel) + } else { + console.log('Selected Element is not a Heading Element') + } + }, }, - setHeadingLevel: async (level: 2 | 1 | 3 | 4 | 5 | 6) => { - const selectedElement = await webflow.getSelectedElement() + setHeadingLevel: { + displayName: 'Set heading level', + code: async (level: 2 | 1 | 3 | 4 | 5 | 6) => { + const selectedElement = await webflow.getSelectedElement() - if (selectedElement?.type === 'Heading') { - const headingLevel = await selectedElement.setHeadingLevel( - parseInt(level) as 2 | 1 | 3 | 4 | 5 | 6, - ) - console.log(headingLevel) - } else { - console.log('Selected Element is not a Heading Element') - } + if (selectedElement?.type === 'Heading') { + const headingLevel = await selectedElement.setHeadingLevel( + parseInt(level) as 2 | 1 | 3 | 4 | 5 | 6, + ) + console.log(headingLevel) + } else { + console.log('Selected Element is not a Heading Element') + } + }, }, }, // Image Operations imageOperations: { - getAsset: async () => { - // Get Selected Element - const el = await webflow.getSelectedElement() - - // Check if element can have children - if (el?.children) { - // Create a new Image Element using Element Presets - const imgEl = await el.append(webflow.elementPresets.Image) - - // Check element type - if (imgEl.type === 'Image') { - // Get asset from Image element - const myAsset = await imgEl.getAsset() - console.log(myAsset) + getAsset: { + displayName: 'Get asset', + code: async () => { + // Get Selected Element + const el = await webflow.getSelectedElement() + + // Check if element can have children + if (el?.children) { + // Create a new Image Element using Element Presets + const imgEl = await el.append(webflow.elementPresets.Image) + + // Check element type + if (imgEl.type === 'Image') { + // Get asset from Image element + const myAsset = await imgEl.getAsset() + console.log(myAsset) + } } - } + }, }, - setAsset: async (assetId: string) => { - // Get Selected Element - const el = await webflow.getSelectedElement() + setAsset: { + displayName: 'Set asset', + code: async (assetId: string) => { + // Get Selected Element + const el = await webflow.getSelectedElement() - // Check if element can have children - if (el?.children) { - // Create a new Image Element using Element Presets - const imgEl = await el.append(webflow.elementPresets.Image) + // Check if element can have children + if (el?.children) { + // Create a new Image Element using Element Presets + const imgEl = await el.append(webflow.elementPresets.Image) - // Get asset by ID - const asset = await webflow.getAssetById(assetId) + // Get asset by ID + const asset = await webflow.getAssetById(assetId) + + // Check element type + if (imgEl.type === 'Image') { + // Set asset as the "src" of the Image Element + await imgEl.setAsset(asset) + } + } + }, + }, + + getAltText: { + displayName: 'Get alt text', + code: async () => { + // Get Selected Element + const el = await webflow.getSelectedElement() + if (el?.type === 'Image') { + // Get alt text + const alt = await el.getAltText() + console.log(alt) + } else { + console.error('Please select an image element') + await webflow.notify({ + type: 'Error', + message: 'Please select an Image Element', + }) + } + }, + }, + + setAltText: { + displayName: 'Set alt text', + code: async (altText: string) => { + // Get Selected Element + const el = await webflow.getSelectedElement() // Check element type - if (imgEl.type === 'Image') { - // Set asset as the "src" of the Image Element - await imgEl.setAsset(asset) - } - } - }, - - getAltText: async () => { - // Get Selected Element - const el = await webflow.getSelectedElement() - if (el?.type === 'Image') { - // Get alt text - const alt = await el.getAltText() - console.log(alt) - } else { - console.error('Please select an image element') - await webflow.notify({ - type: 'Error', - message: 'Please select an Image Element', - }) - } - }, - - setAltText: async (altText: string) => { - // Get Selected Element - const el = await webflow.getSelectedElement() - - // Check element type - if (el?.type === 'Image') { - // Set alt text. If a null is passed, the API will set the alt text as "decorative" - await el.setAltText(altText) - // Get alt text - const alt = await el.getAltText() - console.log(alt) // true - } else { - console.error('Please select an Image Element') - await webflow.notify({ - type: 'Error', - message: 'Please select an Image Element', - }) - } + if (el?.type === 'Image') { + // Set alt text. If a null is passed, the API will set the alt text as "decorative" + await el.setAltText(altText) + // Get alt text + const alt = await el.getAltText() + console.log(alt) // true + } else { + console.error('Please select an Image Element') + await webflow.notify({ + type: 'Error', + message: 'Please select an Image Element', + }) + } + }, }, }, // Link Operations linkOperations: { - setLinkBlockSettings: async (mode: 'url' | 'page' | 'pageSection' | 'email' | 'phone' | 'file', target: string) => { - // Get Selected Element - const element = await webflow.getSelectedElement() - - if (element) { - const newLink = await element.after(webflow.elementPresets.LinkBlock) // Create new link element - const metadata = { openInNewTab: true } - await newLink.setSettings(mode, target, metadata) // Set link element settings - const targetValue = await newLink.getTarget() // Get target value - console.log(targetValue) - } - }, - - getLinkTarget: async () => { - const elements = await webflow.getAllElements() // Get All Elements - const links = elements.filter((element) => element.type === 'Link') // Filter for Link elements - - // Print target value of each link element - for (const link of links) { - const targetValue = await link.getTarget() - console.log(`ID: ${link.id.element}, Target Value: ${targetValue}`) - } - }, - - typeChecking: async () => { - // Example: Type checking in Webflow API - - // Get a selected element - let element = await webflow.getSelectedElement() - - // Check the type of the element - if (element?.type === 'String') { - // It's safe to use methods specific to StringElement - let text = element.getText() - console.log('Text content:', text) - } else if (element?.children) { - // Check if the element can have children - let imageUrl = await element.getChildren() - console.log('Image URL:', imageUrl) - } else { - // Handle other types or default case - console.log( - "Element is not a StringElement and doesn't have child properties", - ) - } + setLinkBlockSettings: { + displayName: 'Set link block settings', + code: async (mode: LinkModeSettings, target: string) => { + // Get Selected Element + const element = await webflow.getSelectedElement() + + if (element) { + const newLink = await element.after(webflow.elementPresets.LinkBlock) // Create new link element + const metadata = { openInNewTab: true } + await newLink.setSettings(mode, target, metadata) // Set link element settings + const targetValue = await newLink.getTarget() // Get target value + console.log(targetValue) + } + }, + }, + + getLinkTarget: { + displayName: 'Get link target', + code: async () => { + const elements = await webflow.getAllElements() // Get All Elements + const links = elements.filter((element) => element.type === 'Link') // Filter for Link elements + + // Print target value of each link element + for (const link of links) { + const targetValue = await link.getTarget() + console.log(`ID: ${link.id.element}, Target Value: ${targetValue}`) + } + }, + }, + + typeChecking: { + displayName: 'Type checking', + code: async () => { + // Example: Type checking in Webflow API + + // Get a selected element + let element = await webflow.getSelectedElement() + + // Check the type of the element + if (element?.type === 'String') { + // It's safe to use methods specific to StringElement + let text = element.getText() + console.log('Text content:', text) + } else if (element?.children) { + // Check if the element can have children + let imageUrl = await element.getChildren() + console.log('Image URL:', imageUrl) + } else { + // Handle other types or default case + console.log( + "Element is not a StringElement and doesn't have child properties", + ) + } + }, }, }, formOperations: { - getFormName: async () => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - if ( - selectedElement?.type === 'FormForm' || - selectedElement?.type === 'FormWrapper' - ) { - const formName = await selectedElement?.getName() - console.log(formName) - } else { - console.log('Selected Element is not a Form') - } - }, - - setFormName: async (name: string) => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - if ( - selectedElement?.type === 'FormForm' || - selectedElement?.type === 'FormWrapper' - ) { - await selectedElement?.setName(name) - } - }, - getFormSettings: async () => { - const selectedElement = await webflow.getSelectedElement() - if ( - selectedElement && - 'getSettings' in selectedElement && - (selectedElement.type === 'FormForm' || - selectedElement.type === 'FormWrapper') - ) { - const settings = await selectedElement.getSettings() - console.log(settings) - } else { - console.log('Selected element is not a Form or Form Wrapper.') - } - }, - - setFormSettings: async () => { - const selectedElement = await webflow.getSelectedElement() - if ( - selectedElement && - 'setSettings' in selectedElement && - (selectedElement.type === 'FormForm' || - selectedElement.type === 'FormWrapper') - ) { - await selectedElement.setSettings({ - action: 'https://webhook.site/be45a9c3-9ada-4d1a-b91d-5616bcb65448', - method: 'post', - state: 'normal', - name: 'Contact Form', - }) - console.log('Form settings updated.') - } else { - console.log('Selected element is not a Form or Form Wrapper.') - } + getFormName: { + displayName: 'Get form name', + code: async () => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() + + if ( + selectedElement?.type === 'FormForm' || + selectedElement?.type === 'FormWrapper' + ) { + const formName = await selectedElement?.getName() + console.log(formName) + } else { + console.log('Selected Element is not a Form') + } + }, }, - createFormWithTextarea: async () => { - // Get the selected element to attach the form to - const selectedElement = await webflow.getSelectedElement() + setFormName: { + displayName: 'Set form name', + code: async (name: string) => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() - if (selectedElement && selectedElement.children) { - // Create the form block by appending the FormForm preset - const formWrapper = await selectedElement.append( - webflow.elementPresets.FormForm, - ) + if ( + selectedElement?.type === 'FormForm' || + selectedElement?.type === 'FormWrapper' + ) { + await selectedElement?.setName(name) + } + }, + }, + + getFormSettings: { + displayName: 'Get form settings', + code: async () => { + const selectedElement = await webflow.getSelectedElement() + if ( + selectedElement && + 'getSettings' in selectedElement && + (selectedElement.type === 'FormForm' || + selectedElement.type === 'FormWrapper') + ) { + const settings = await selectedElement.getSettings() + console.log(settings) + } else { + console.log('Selected element is not a Form or Form Wrapper.') + } + }, + }, + + setFormSettings: { + displayName: 'Set form settings', + code: async () => { + const selectedElement = await webflow.getSelectedElement() + if ( + selectedElement && + 'setSettings' in selectedElement && + (selectedElement.type === 'FormForm' || + selectedElement.type === 'FormWrapper') + ) { + await selectedElement.setSettings({ + action: 'https://webhook.site/be45a9c3-9ada-4d1a-b91d-5616bcb65448', + method: 'post', + state: 'normal', + name: 'Contact Form', + }) + console.log('Form settings updated.') + } else { + console.log('Selected element is not a Form or Form Wrapper.') + } + }, + }, - // The FormForm preset creates a FormWrapper that contains the Form element - // We need to get the actual form element to append fields to it - if (formWrapper.type === 'FormWrapper') { - const children = await formWrapper.getChildren() - const form = children.find((child) => child.type === 'FormForm') + createFormWithTextarea: { + displayName: 'Create form with textarea', + code: async () => { + // Get the selected element to attach the form to + const selectedElement = await webflow.getSelectedElement() - if (form && form.children) { - const formChildren = await form.getChildren() - const submitButton = formChildren.find( - (child) => child.type === 'FormButton', - ) + if (selectedElement && selectedElement.children) { + // Create the form block by appending the FormForm preset + const formWrapper = await selectedElement.append( + webflow.elementPresets.FormForm, + ) + + // The FormForm preset creates a FormWrapper that contains the Form element + if (formWrapper.type === 'FormWrapper') { + const children = await formWrapper.getChildren() + const form = children.find((child) => child.type === 'FormForm') - if (submitButton) { - // Create and insert a label before the submit button - const label = await submitButton.before( - webflow.elementPresets.FormBlockLabel, + if (form && form.children) { + const formChildren = await form.getChildren() + const submitButton = formChildren.find( + (child) => child.type === 'FormButton', ) - if (label.type === 'FormBlockLabel') { - await label.setTextContent('Your Message') - } - // Create and insert a textarea before the submit button - await submitButton.before(webflow.elementPresets.FormTextarea) + if (submitButton) { + // Create and insert a label before the submit button + const label = await submitButton.before( + webflow.elementPresets.FormBlockLabel, + ) + if (label.type === 'FormBlockLabel') { + await label.setTextContent('Your Message') + } + + // Create and insert a textarea before the submit button + await submitButton.before(webflow.elementPresets.FormTextarea) + } } } + console.log('Form with textarea created successfully.') + } else { + console.log('Please select an element that can contain children.') } - console.log('Form with textarea created successfully.') - } else { - console.log('Please select an element that can contain children.') - } + }, }, }, + // Display Name Operations displayName: { - getDisplayName: async () => { - // Get Selected Element - const element = await webflow.getSelectedElement() - - if (element && element.displayName) { - // Get the Navigator display name (null if using automatic name) - const name = await element.getDisplayName() - console.log(name) // e.g. 'Hero Wrapper' or null - } else { - console.log('No element selected or element does not support display names') - } - }, - - setDisplayName: async (displayName: string) => { - // Get Selected Element - const element = await webflow.getSelectedElement() - - if (element && element.displayName) { - // Set a custom Navigator label; pass '' to reset to automatic name - await element.setDisplayName(displayName) - - // Verify the update - const name = await element.getDisplayName() - console.log(name) - } else { - console.log('No element selected or element does not support display names') - } + getDisplayName: { + displayName: 'Get display name', + code: async () => { + // Get Selected Element + const element = await webflow.getSelectedElement() + + if (element && element.displayName) { + // Get the Navigator display name (null if using automatic name) + const name = await element.getDisplayName() + console.log(name) // e.g. 'Hero Wrapper' or null + } else { + console.log('No element selected or element does not support display names') + } + }, + }, + + setDisplayName: { + displayName: 'Set display name', + code: async (displayName: string) => { + // Get Selected Element + const element = await webflow.getSelectedElement() + + if (element && element.displayName) { + // Set a custom Navigator label; pass '' to reset to automatic name + await element.setDisplayName(displayName) + + // Verify the update + const name = await element.getDisplayName() + console.log(name) + } else { + console.log('No element selected or element does not support display names') + } + }, }, }, formInputOperations: { - getInputName: async () => { - const selectedElement = await webflow.getSelectedElement() - if (selectedElement && 'getName' in selectedElement) { - const name = await selectedElement.getName() - console.log(name) - } else { - console.log('Selected element does not have a getName method.') - } - }, - - setInputName: async (name: string) => { - const selectedElement = await webflow.getSelectedElement() - if (selectedElement && 'setName' in selectedElement) { - await selectedElement.setName(name) - console.log(`Input name set to: ${name}`) - } else { - console.log('Selected element does not have a setName method.') - } - }, - isRequired: async () => { - const selectedElement = await webflow.getSelectedElement() - if (selectedElement && 'getRequired' in selectedElement) { - const required = await selectedElement.getRequired() - console.log(`Is required: ${required}`) - } else { - console.log('Selected element does not have an isRequired method.') - } - }, - - setIsRequired: async (required: boolean) => { - const selectedElement = await webflow.getSelectedElement() - if (selectedElement && 'setRequired' in selectedElement) { - await selectedElement.setRequired(required) - console.log(`Is required set to: ${required}`) - } else { - console.log('Selected element does not have a setIsRequired method.') - } - }, - createSelectWithCustomDOM: async () => { - const selectedElement = await webflow.getSelectedElement() - - if (selectedElement && selectedElement.children) { - // Create a wrapper div using the element builder - const wrapper = webflow.elementBuilder(webflow.elementPresets.DOM) - wrapper.setTag('div') - - // Create a select element using the DOM preset and append it to the wrapper - const select = wrapper.append(webflow.elementPresets.DOM) - select.setTag('select') - select.setAttribute('name', 'custom-select') - - // Define the options for the select field - const choices = [ - { name: 'First choice', value: 'first' }, - { name: 'Second choice', value: 'second' }, - { name: 'Third choice', value: 'third' }, - ] + getInputName: { + displayName: 'Get input name', + code: async () => { + const selectedElement = await webflow.getSelectedElement() + if (selectedElement && 'getName' in selectedElement) { + const name = await selectedElement.getName() + console.log(name) + } else { + console.log('Selected element does not have a getName method.') + } + }, + }, - // Create and append option elements - choices.forEach((choice) => { - const option = select.append(webflow.elementPresets.DOM) - option.setTag('option') - option.setAttribute('value', choice.value) - option.setTextContent(choice.name) - }) + setInputName: { + displayName: 'Set input name', + code: async (name: string) => { + const selectedElement = await webflow.getSelectedElement() + if (selectedElement && 'setName' in selectedElement) { + await selectedElement.setName(name) + console.log(`Input name set to: ${name}`) + } else { + console.log('Selected element does not have a setName method.') + } + }, + }, - // Add the wrapper with the custom select element to the page - const wrapperElement = await selectedElement.append(wrapper) + isRequired: { + displayName: 'Is required', + code: async () => { + const selectedElement = await webflow.getSelectedElement() + if (selectedElement && 'getRequired' in selectedElement) { + const required = await selectedElement.getRequired() + console.log(`Is required: ${required}`) + } else { + console.log('Selected element does not have an isRequired method.') + } + }, + }, - if (wrapperElement && wrapperElement.children) { - // Prepend a FormBlockLabel to the wrapper - const label = await wrapperElement.prepend( - webflow.elementPresets.FormBlockLabel, - ) - if (label.type === 'FormBlockLabel') { - await label.setTextContent('Custom Select Field') - } + setIsRequired: { + displayName: 'Set is required', + code: async (required: boolean) => { + const selectedElement = await webflow.getSelectedElement() + if (selectedElement && 'setRequired' in selectedElement) { + await selectedElement.setRequired(required) + console.log(`Is required set to: ${required}`) + } else { + console.log('Selected element does not have a setIsRequired method.') } + }, + }, + + createSelectWithCustomDOM: { + displayName: 'Create select with custom DOM', + code: async () => { + const selectedElement = await webflow.getSelectedElement() + + if (selectedElement && selectedElement.children) { + // Create a wrapper div using the element builder + const wrapper = webflow.elementBuilder(webflow.elementPresets.DOM) + wrapper.setTag('div') + + // Create a select element using the DOM preset and append it to the wrapper + const select = wrapper.append(webflow.elementPresets.DOM) + select.setTag('select') + select.setAttribute('name', 'custom-select') + + // Define the options for the select field + const choices = [ + { name: 'First choice', value: 'first' }, + { name: 'Second choice', value: 'second' }, + { name: 'Third choice', value: 'third' }, + ] + + // Create and append option elements + choices.forEach((choice) => { + const option = select.append(webflow.elementPresets.DOM) + option.setTag('option') + option.setAttribute('value', choice.value) + option.setTextContent(choice.name) + }) + + // Add the wrapper with the custom select element to the page + const wrapperElement = await selectedElement.append(wrapper) + + if (wrapperElement && wrapperElement.children) { + // Prepend a FormBlockLabel to the wrapper + const label = await wrapperElement.prepend( + webflow.elementPresets.FormBlockLabel, + ) + if (label.type === 'FormBlockLabel') { + await label.setTextContent('Custom Select Field') + } + } - console.log( - 'Custom select field with wrapper and label created successfully.', - ) - } else { - console.log('Please select an element that can contain child elements.') - } + console.log( + 'Custom select field with wrapper and label created successfully.', + ) + } else { + console.log('Please select an element that can contain child elements.') + } + }, }, }, // Bindable Sources bindableSources: { - searchBindableSources: async () => { - // Get Selected Element - const element = await webflow.getSelectedElement() - - if (element && 'searchBindableSources' in element) { - // Get all bindable sources available for this element - const sources = await element.searchBindableSources() - console.log(`Found ${sources.length} bindable source(s):`) - console.log(sources) - } else { - console.log('Selected element does not support searchBindableSources.') - } - }, - - searchBindableSourcesBySettingKey: async (settingKey: string) => { - // Get Selected Element - const element = await webflow.getSelectedElement() - - if (element && 'searchBindableSources' in element) { - // Filter bindable sources to those compatible with a specific setting key - // e.g. "src", "altText", "domId" - const sources = await element.searchBindableSources({ settingKey }) - console.log( - `Found ${sources.length} bindable source(s) for settingKey "${settingKey}":`, - ) - console.log(sources) - } else { - console.log('Selected element does not support searchBindableSources.') - } + searchBindableSources: { + displayName: 'Search bindable sources', + code: async () => { + // Get Selected Element + const element = await webflow.getSelectedElement() + + if (element && 'searchBindableSources' in element) { + // Get all bindable sources available for this element + const sources = await element.searchBindableSources() + console.log(`Found ${sources.length} bindable source(s):`) + console.log(sources) + } else { + console.log('Selected element does not support searchBindableSources.') + } + }, }, - searchBindableSourcesByValueType: async (valueType: string) => { - // Get Selected Element - const element = await webflow.getSelectedElement() + searchBindableSourcesBySettingKey: { + displayName: 'Search bindable sources by setting key', + code: async (settingKey: string) => { + // Get Selected Element + const element = await webflow.getSelectedElement() - if (element && 'searchBindableSources' in element) { - // Filter bindable sources to those that produce a specific value type - // e.g. "text", "imageAsset", "boolean", "link" - const sources = await element.searchBindableSources({ valueType }) - console.log( - `Found ${sources.length} bindable source(s) with valueType "${valueType}":`, - ) - console.log(sources) - } else { - console.log('Selected element does not support searchBindableSources.') - } - }, - - searchBindableSourcesWithBothFilters: async ( - settingKey: string, - valueType: string, - ) => { - // Get Selected Element - const element = await webflow.getSelectedElement() - - if (element && 'searchBindableSources' in element) { - // Combine settingKey and valueType filters to narrow results further - const sources = await element.searchBindableSources({ - settingKey, - valueType, - }) - console.log( - `Found ${sources.length} bindable source(s) for settingKey "${settingKey}" and valueType "${valueType}":`, - ) - console.log(sources) - } else { - console.log('Selected element does not support searchBindableSources.') - } + if (element && 'searchBindableSources' in element) { + // Filter bindable sources to those compatible with a specific setting key + // e.g. "src", "altText", "domId" + const sources = await element.searchBindableSources({ settingKey }) + console.log( + `Found ${sources.length} bindable source(s) for settingKey "${settingKey}":`, + ) + console.log(sources) + } else { + console.log('Selected element does not support searchBindableSources.') + } + }, + }, + + searchBindableSourcesByValueType: { + displayName: 'Search bindable sources by value type', + code: async (valueType: string) => { + // Get Selected Element + const element = await webflow.getSelectedElement() + + if (element && 'searchBindableSources' in element) { + // Filter bindable sources to those that produce a specific value type + // e.g. "text", "imageAsset", "boolean", "link" + const sources = await element.searchBindableSources({ valueType }) + console.log( + `Found ${sources.length} bindable source(s) with valueType "${valueType}":`, + ) + console.log(sources) + } else { + console.log('Selected element does not support searchBindableSources.') + } + }, + }, + + searchBindableSourcesWithBothFilters: { + displayName: 'Search bindable sources with both filters', + code: async ( + settingKey: string, + valueType: string, + ) => { + // Get Selected Element + const element = await webflow.getSelectedElement() + + if (element && 'searchBindableSources' in element) { + // Combine settingKey and valueType filters to narrow results further + const sources = await element.searchBindableSources({ + settingKey, + valueType, + }) + console.log( + `Found ${sources.length} bindable source(s) for settingKey "${settingKey}" and valueType "${valueType}":`, + ) + console.log(sources) + } else { + console.log('Selected element does not support searchBindableSources.') + } + }, }, }, // Element Settings elementSettings: { - searchSettings: async () => { - // Get Selected Element - const element = await webflow.getSelectedElement() - - if (element && 'searchSettings' in element) { - // Get all settings for this element, including current values and display metadata - const settings = await element.searchSettings() - const keys = Object.keys(settings) - console.log(`Found ${keys.length} setting(s):`) - console.log(settings) - } else { - console.log('Selected element does not support searchSettings.') - } - }, - - searchSettingsByKey: async (key: string) => { - // Get Selected Element - const element = await webflow.getSelectedElement() - - if (element && 'searchSettings' in element) { - // Filter settings to a specific setting key, e.g. "assetId", "domId", "visibility" - const settings = await element.searchSettings({ key }) - const keys = Object.keys(settings) - console.log(`Found ${keys.length} setting(s) for key "${key}":`) - console.log(settings) - } else { - console.log('Selected element does not support searchSettings.') - } - }, - - searchSettingsByValueType: async (valueType: string) => { - // Get Selected Element - const element = await webflow.getSelectedElement() - - if (element && 'searchSettings' in element) { - // Filter settings to those with a specific value type, e.g. "string", "image", "boolean" - const settings = await element.searchSettings({ valueType }) - const keys = Object.keys(settings) - console.log( - `Found ${keys.length} setting(s) with valueType "${valueType}":`, - ) - console.log(settings) - } else { - console.log('Selected element does not support searchSettings.') - } + searchSettings: { + displayName: 'Search settings', + code: async () => { + // Get Selected Element + const element = await webflow.getSelectedElement() + + if (element && 'searchSettings' in element) { + // Get all settings for this element, including current values and display metadata + const settings = await element.searchSettings() + const keys = Object.keys(settings) + console.log(`Found ${keys.length} setting(s):`) + console.log(settings) + } else { + console.log('Selected element does not support searchSettings.') + } + }, }, - searchSettingsWithBothFilters: async (key: string, valueType: string) => { - // Get Selected Element - const element = await webflow.getSelectedElement() + searchSettingsByKey: { + displayName: 'Search settings by key', + code: async (key: string) => { + // Get Selected Element + const element = await webflow.getSelectedElement() - if (element && 'searchSettings' in element) { - // Combine key and valueType filters to narrow results further - const settings = await element.searchSettings({ key, valueType }) - const keys = Object.keys(settings) - console.log( - `Found ${keys.length} setting(s) for key "${key}" and valueType "${valueType}":`, - ) - console.log(settings) - } else { - console.log('Selected element does not support searchSettings.') - } + if (element && 'searchSettings' in element) { + // Filter settings to a specific setting key, e.g. "assetId", "domId", "visibility" + const settings = await element.searchSettings({ key }) + const keys = Object.keys(settings) + console.log(`Found ${keys.length} setting(s) for key "${key}":`) + console.log(settings) + } else { + console.log('Selected element does not support searchSettings.') + } + }, }, - getSettings: async () => { - // Get Selected Element - const element = await webflow.getSelectedElement() + searchSettingsByValueType: { + displayName: 'Search settings by value type', + code: async (valueType: string) => { + // Get Selected Element + const element = await webflow.getSelectedElement() - if (element && 'getSettings' in element) { - // Get the raw setting values for the element, including any binding references - const settings = await element.getSettings() - console.log(settings) - } else { - console.log('Selected element does not support getSettings.') - } + if (element && 'searchSettings' in element) { + // Filter settings to those with a specific value type, e.g. "string", "image", "boolean" + const settings = await element.searchSettings({ valueType }) + const keys = Object.keys(settings) + console.log( + `Found ${keys.length} setting(s) with valueType "${valueType}":`, + ) + console.log(settings) + } else { + console.log('Selected element does not support searchSettings.') + } + }, }, - getResolvedSettings: async () => { - // Get Selected Element - const element = await webflow.getSelectedElement() + searchSettingsWithBothFilters: { + displayName: 'Search settings with both filters', + code: async (key: string, valueType: string) => { + // Get Selected Element + const element = await webflow.getSelectedElement() - if (element && 'getResolvedSettings' in element) { - // Get the resolved setting values for the element - // Unlike getSettings(), this resolves any binding references to their computed values - const settings = await element.getResolvedSettings() - console.log(settings) - } else { - console.log('Selected element does not support getResolvedSettings.') - } + if (element && 'searchSettings' in element) { + // Combine key and valueType filters to narrow results further + const settings = await element.searchSettings({ key, valueType }) + const keys = Object.keys(settings) + console.log( + `Found ${keys.length} setting(s) for key "${key}" and valueType "${valueType}":`, + ) + console.log(settings) + } else { + console.log('Selected element does not support searchSettings.') + } + }, }, - setSettings: async () => { - // Get Selected Element - const element = await webflow.getSelectedElement() + getSettings: { + displayName: 'Get settings', + code: async () => { + // Get Selected Element + const element = await webflow.getSelectedElement() - if (element && 'setSettings' in element) { - // Set a static value - await element.setSettings({ tag: 'h2', domId: 'my-heading' }) - - // Bind a setting to a component property - // await element.setSettings({ domId: { sourceType: 'prop', propId: 'prop_3' } }) + if (element && 'getSettings' in element) { + // Get the raw setting values for the element, including any binding references + const settings = await element.getSettings() + console.log(settings) + } else { + console.log('Selected element does not support getSettings.') + } + }, + }, - // Reset a setting to its default value and disconnect any binding - // await element.setSettings({ altText: null }) + getResolvedSettings: { + displayName: 'Get resolved settings', + code: async () => { + // Get Selected Element + const element = await webflow.getSelectedElement() - console.log('Element settings updated.') - } else { - console.log('Selected element does not support setSettings.') - } + if (element && 'getResolvedSettings' in element) { + // Get the resolved setting values for the element + // Unlike getSettings(), this resolves any binding references to their computed values + const settings = await element.getResolvedSettings() + console.log(settings) + } else { + console.log('Selected element does not support getResolvedSettings.') + } + }, }, - getVisibility: async () => { - const element = await webflow.getSelectedElement(); + setSettings: { + displayName: 'Set settings', + code: async () => { + // Get Selected Element + const element = await webflow.getSelectedElement() - if (element?.visibility) { + if (element && 'setSettings' in element) { + // Set a static value + await element.setSettings({ tag: 'h2', domId: 'my-heading' }) - // Check current visibility - const isVisible = await element.getVisibility(); - console.log(isVisible); // true or false + // Bind a setting to a component property + // await element.setSettings({ domId: { sourceType: 'prop', propId: 'prop_3' } }) - // Read binding metadata - const value = await element.getVisibility({ bindings: true }); - // Returns BindingValue when bound, or boolean when static - } + // Reset a setting to its default value and disconnect any binding + // await element.setSettings({ altText: null }) + + console.log('Element settings updated.') + } else { + console.log('Selected element does not support setSettings.') + } + }, }, - setVisibility: async () => { - const element = await webflow.getSelectedElement(); + getVisibility: { + displayName: 'Get visibility', + code: async () => { + const element = await webflow.getSelectedElement(); + + if (element?.visibility) { + // Check current visibility + const isVisible = await element.getVisibility(); + console.log(isVisible); // true or false - if (element?.visibility) { + // Read binding metadata + const value = await element.getVisibility({ bindings: true }); + // Returns BindingValue when bound, or boolean when static + } + }, + }, - // Hide the element - await element.setVisibility(false); + setVisibility: { + displayName: 'Set visibility', + code: async () => { + const element = await webflow.getSelectedElement(); - // Show the element - await element.setVisibility(true); - } + if (element?.visibility) { + // Hide the element + await element.setVisibility(false); + + // Show the element + await element.setVisibility(true); + } + }, }, }, } diff --git a/src/examples/examples.ts b/src/examples/examples.ts index d511408..9f9c903 100644 --- a/src/examples/examples.ts +++ b/src/examples/examples.ts @@ -10,7 +10,7 @@ import { Assets } from './assets' import { ValidFileTypesEnum } from './assets' // Add default example for createAssetFromURL -Assets.assetCreation.createAssetFromURL.example = { +Assets.assetCreation.createAssetFromURL.code.example = { url: 'https://picsum.photos/200', fileName: 'example-image.jpg', fileTypeEnum: ValidFileTypesEnum.JPEG, diff --git a/src/examples/folders.ts b/src/examples/folders.ts index 0aa1a4c..70d3d74 100644 --- a/src/examples/folders.ts +++ b/src/examples/folders.ts @@ -1,81 +1,101 @@ export const Folders = { folders: { - createNewFolder: async (name: string) => { - // Create and name new folder - const newFolder = await webflow.createPageFolder() - await newFolder.setName(name) + createNewFolder: { + displayName: 'Create new folder', + code: async (name: string) => { + // Create and name new folder + const newFolder = await webflow.createPageFolder() + await newFolder.setName(name) - // Print details - const folderName = await newFolder.getName() - console.log(folderName) + // Print details + const folderName = await newFolder.getName() + console.log(folderName) + }, }, - getFolderName: async () => { - // Get all Pages and folders - const pagesAndFolders = await webflow.getAllPagesAndFolders() + getFolderName: { + displayName: 'Get folder name', + code: async () => { + // Get all Pages and folders + const pagesAndFolders = await webflow.getAllPagesAndFolders() - for (let folder of pagesAndFolders) { - const folderName = await folder.getName() - const type = folder.type - console.log(folderName, type) - } + for (let folder of pagesAndFolders) { + const folderName = await folder.getName() + const type = folder.type + console.log(folderName, type) + } + }, }, - setFolderName: async (name: string) => { - // Create and name new folder - const newFolder = await webflow.createPageFolder() - await newFolder.setName(name) + setFolderName: { + displayName: 'Set folder name', + code: async (name: string) => { + // Create and name new folder + const newFolder = await webflow.createPageFolder() + await newFolder.setName(name) - // Print details - const folderName = await newFolder.getName() - console.log(folderName) + // Print details + const folderName = await newFolder.getName() + console.log(folderName) + }, }, - }, slugs: { - getSlug: async () => { - // Get all Pages and folders - const pagesAndFolders = await webflow.getAllPagesAndFolders() + getSlug: { + displayName: 'Get slug', + code: async () => { + // Get all Pages and folders + const pagesAndFolders = await webflow.getAllPagesAndFolders() - for (let folder of pagesAndFolders) { - const folderSlug = await folder.getSlug() - console.log('Slug', folderSlug) - } + for (let folder of pagesAndFolders) { + const folderSlug = await folder.getSlug() + console.log('Slug', folderSlug) + } + }, }, - setSlug: async (slug: string) => { - // Create and give slug to new folder - const newFolder = await webflow.createPageFolder() - await newFolder.setSlug(slug) + setSlug: { + displayName: 'Set slug', + code: async (slug: string) => { + // Create and give slug to new folder + const newFolder = await webflow.createPageFolder() + await newFolder.setSlug(slug) - // Print details - const newSlug = await newFolder.getSlug() - console.log('Slug', newSlug) + // Print details + const newSlug = await newFolder.getSlug() + console.log('Slug', newSlug) + }, }, }, parents: { - getParentFolder: async () => { - // Get all Pages and folders - const pagesAndFolders = await webflow.getAllPagesAndFolders() + getParentFolder: { + displayName: 'Get parent folder', + code: async () => { + // Get all Pages and folders + const pagesAndFolders = await webflow.getAllPagesAndFolders() - for (let folder of pagesAndFolders) { - const childName = await folder.getName() - const parent = await folder.getParent() - const parentName = await parent?.getName() - console.log(`Folder: ${childName}`, `Parent: ${parentName}`) - } + for (let folder of pagesAndFolders) { + const childName = await folder.getName() + const parent = await folder.getParent() + const parentName = await parent?.getName() + console.log(`Folder: ${childName}`, `Parent: ${parentName}`) + } + }, }, - setParentFolder: async (folderName: string) => { - // Get all Pages and folders - const pagesAndFolders = await webflow.getAllPagesAndFolders() + setParentFolder: { + displayName: 'Set parent folder', + code: async (folderName: string) => { + // Get all Pages and folders + const pagesAndFolders = await webflow.getAllPagesAndFolders() - // Create and name new folder - const newFolder = await webflow.createPageFolder() - await newFolder.setName(folderName) + // Create and name new folder + const newFolder = await webflow.createPageFolder() + await newFolder.setName(folderName) - for (let folder of pagesAndFolders) { - await folder.setParent(newFolder) - } + for (let folder of pagesAndFolders) { + await folder.setParent(newFolder) + } + }, }, }, } diff --git a/src/examples/pages.ts b/src/examples/pages.ts index 1f6c0d0..d0a0dd7 100644 --- a/src/examples/pages.ts +++ b/src/examples/pages.ts @@ -36,330 +36,414 @@ export const getPageByName = async ( export const Pages = { // Page Management pageManagement: { - getCurrentPage: async () => { - // Get Current Page - const currentPage = await webflow.getCurrentPage() - const pageName = await currentPage?.getName() + getCurrentPage: { + displayName: 'Get current page', + code: async () => { + // Get Current Page + const currentPage = await webflow.getCurrentPage() + const pageName = await currentPage?.getName() - // Print page details - console.log(currentPage, pageName) + // Print page details + console.log(currentPage, pageName) + }, }, - getAllPagesFolders: async () => { - // Get all pages and folders - const pagesAndFolders = await webflow.getAllPagesAndFolders() - - // Print Page Details - const pages = pagesAndFolders?.filter((i) => i.type === 'Page') - await Promise.all( - pages.map(async (page) => { - const pageName = await page.getName() - console.log(`Page: ${pageName}`) - }), - ) - - const folders = pagesAndFolders?.filter((i) => i.type === 'PageFolder') - await Promise.all( - folders.map(async (folder) => { - const folderName = await folder.getName() - console.log(`Folder: ${folderName}`) - }), - ) + getAllPagesFolders: { + displayName: 'Get all pages folders', + code: async () => { + // Get all pages and folders + const pagesAndFolders = await webflow.getAllPagesAndFolders() + + // Print Page Details + const pages = pagesAndFolders?.filter((i) => i.type === 'Page') + await Promise.all( + pages.map(async (page) => { + const pageName = await page.getName() + console.log(`Page: ${pageName}`) + }), + ) + + const folders = pagesAndFolders?.filter((i) => i.type === 'PageFolder') + await Promise.all( + folders.map(async (folder) => { + const folderName = await folder.getName() + console.log(`Folder: ${folderName}`) + }), + ) + }, }, - switchPage: async () => { - // Get All Pages and Folders - const pagesAndFolders = await webflow.getAllPagesAndFolders() - const pages = pagesAndFolders?.filter((i): i is Page => i.type === 'Page') - - // Switch Page - const newPage = pages[2] - await webflow.switchPage(newPage) + switchPage: { + displayName: 'Switch page', + code: async () => { + // Get All Pages and Folders + const pagesAndFolders = await webflow.getAllPagesAndFolders() + const pages = pagesAndFolders?.filter((i): i is Page => i.type === 'Page') + + // Switch Page + const newPage = pages[2] + await webflow.switchPage(newPage) + }, }, - createNewPage: async () => { - // Create new page and set page name - const newPage = await webflow.createPage() - if (!newPage) return - - // Set page name and switch to it - await newPage.setName('My New Page') - await webflow.switchPage(newPage) + createNewPage: { + displayName: 'Create new page', + code: async () => { + // Create new page and set page name + const newPage = await webflow.createPage() + if (!newPage) return + + // Set page name and switch to it + await newPage.setName('My New Page') + await webflow.switchPage(newPage) + }, }, }, // Page Information pageInformation: { - getPageKind: async () => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + getPageKind: { + displayName: 'Get page kind', + code: async () => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Get the page - const pageKind = await currentPage.getKind() - console.log(`Page Category: ${pageKind}`) + // Get the page + const pageKind = await currentPage.getKind() + console.log(`Page Category: ${pageKind}`) + }, }, - getPageName: async () => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + getPageName: { + displayName: 'Get page name', + code: async () => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Get page name - const pageName = await currentPage.getName() - console.log(pageName) + // Get page name + const pageName = await currentPage.getName() + console.log(pageName) + }, }, - setPageName: async (pageName: string) => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + setPageName: { + displayName: 'Set page name', + code: async (pageName: string) => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Set page name - await currentPage.setName(pageName) - console.log(pageName) + // Set page name + await currentPage.setName(pageName) + console.log(pageName) + }, }, - getPageTitle: async () => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + getPageTitle: { + displayName: 'Get page title', + code: async () => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Get page title - const pageTitle = await currentPage.getTitle() - console.log(pageTitle) + // Get page title + const pageTitle = await currentPage.getTitle() + console.log(pageTitle) + }, }, - setPageTitle: async (pageTitle: string) => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + setPageTitle: { + displayName: 'Set page title', + code: async (pageTitle: string) => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Set page Title - await currentPage.setTitle(pageTitle) - console.log(pageTitle) + // Set page Title + await currentPage.setTitle(pageTitle) + console.log(pageTitle) + }, }, - getPageDescription: async () => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + getPageDescription: { + displayName: 'Get page description', + code: async () => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Get page Description - const pageDescription = await currentPage.getDescription() - console.log(pageDescription) + // Get page Description + const pageDescription = await currentPage.getDescription() + console.log(pageDescription) + }, }, - setPageDescription: async (pageDescription: string) => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + setPageDescription: { + displayName: 'Set page description', + code: async (pageDescription: string) => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Set page Description - await currentPage.setDescription(pageDescription) - console.log(pageDescription) + // Set page Description + await currentPage.setDescription(pageDescription) + console.log(pageDescription) + }, }, - getPageSlug: async () => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + getPageSlug: { + displayName: 'Get page slug', + code: async () => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Get page slug - const pageSlug = await currentPage.getSlug() - console.log(pageSlug) + // Get page slug + const pageSlug = await currentPage.getSlug() + console.log(pageSlug) + }, }, - setPageSlug: async (slug: string) => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + setPageSlug: { + displayName: 'Set page slug', + code: async (slug: string) => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Set page Description - await currentPage.setSlug(slug) - const newSlug = await currentPage.getSlug() - console.log('Slug', newSlug) + // Set page Description + await currentPage.setSlug(slug) + const newSlug = await currentPage.getSlug() + console.log('Slug', newSlug) + }, }, }, // Collection Information collectionInformation: { - getCollectionId: async () => { - try { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page - - // Get Collection ID if page belongs to a collection - const collectionId = await currentPage.getCollectionId() - console.log(collectionId) - } catch (err) { - const error = err as { cause: { tag: string }; message: string } - console.error([error.cause.tag, error.message]) - } + getCollectionId: { + displayName: 'Get collection ID', + code: async () => { + try { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page + + // Get Collection ID if page belongs to a collection + const collectionId = await currentPage.getCollectionId() + console.log(collectionId) + } catch (err) { + const error = err as { cause: { tag: string }; message: string } + console.error([error.cause.tag, error.message]) + } + }, }, - getCollectionName: async () => { - try { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page - - // Get Collection ID if page belongs to a collection - const collectionName = await currentPage.getCollectionName() - console.log(collectionName) - } catch (err) { - const error = err as { cause: { tag: string }; message: string } - console.error([error.cause.tag, error.message]) - } + getCollectionName: { + displayName: 'Get collection name', + code: async () => { + try { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page + + // Get Collection ID if page belongs to a collection + const collectionName = await currentPage.getCollectionName() + console.log(collectionName) + } catch (err) { + const error = err as { cause: { tag: string }; message: string } + console.error([error.cause.tag, error.message]) + } + }, }, }, // Page Status pageStatus: { - checkIfDraft: async () => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page - - // Check page status - const isDraft = await currentPage.isDraft() - - // Print page status - if (isDraft) { - console.log('Page is draft') - } else { - console.log('Page is not a draft') - } + checkIfDraft: { + displayName: 'Check if draft', + code: async () => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page + + // Check page status + const isDraft = await currentPage.isDraft() + + // Print page status + if (isDraft) { + console.log('Page is draft') + } else { + console.log('Page is not a draft') + } + }, }, - setAsDraft: async () => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + setAsDraft: { + displayName: 'Set as draft', + code: async () => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Set page as draft - await currentPage.setDraft(true) - const isDraft = await currentPage.isDraft() + // Set page as draft + await currentPage.setDraft(true) + const isDraft = await currentPage.isDraft() - // Print status - console.log(isDraft) + // Print status + console.log(isDraft) + }, }, - checkIfPassword: async () => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + checkIfPassword: { + displayName: 'Check if password protected', + code: async () => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Check if current page is the homepage - const isPasswordProtected = await currentPage.isPasswordProtected() + // Check if current page is the homepage + const isPasswordProtected = await currentPage.isPasswordProtected() - if (isPasswordProtected) { - console.log('Current page is PasswordProtected') - } else { - console.log('Current page is not PasswordProtected') - } + if (isPasswordProtected) { + console.log('Current page is PasswordProtected') + } else { + console.log('Current page is not PasswordProtected') + } + }, }, - isPageHomePage: async () => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + isPageHomePage: { + displayName: 'Is page home page', + code: async () => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Check if current page is the homepage - const isHomepage = await currentPage.isHomepage() + // Check if current page is the homepage + const isHomepage = await currentPage.isHomepage() - if (isHomepage) { - console.log('Current page is the Homepage') - } else { - console.log('Current page is not the Homepage') - } + if (isHomepage) { + console.log('Current page is the Homepage') + } else { + console.log('Current page is not the Homepage') + } + }, }, - getUtilityPageKey: async () => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + getUtilityPageKey: { + displayName: 'Get utility page key', + code: async () => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Get Utility Key - const utilityKey = await currentPage.getUtilityPageKey() - console.log('Utility Key', utilityKey) + // Get Utility Key + const utilityKey = await currentPage.getUtilityPageKey() + console.log('Utility Key', utilityKey) + }, }, }, // Open Graph Settings openGraphSettings: { - checkOpenGraphTitle: async () => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page - - // Check if page is using the Title as the Open Graph title - const isOpenGraphTitle = await currentPage.usesTitleAsOpenGraphTitle() - - // Print results - if (isOpenGraphTitle) { - console.log('Page uses Title as Open Graph Title') - } else { - console.log('This page has a custom Open Graph Title') - } + checkOpenGraphTitle: { + displayName: 'Check Open Graph title', + code: async () => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page + + // Check if page is using the Title as the Open Graph title + const isOpenGraphTitle = await currentPage.usesTitleAsOpenGraphTitle() + + // Print results + if (isOpenGraphTitle) { + console.log('Page uses Title as Open Graph Title') + } else { + console.log('This page has a custom Open Graph Title') + } + }, }, - setTitleAsOpenGraphTitle: async () => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + setTitleAsOpenGraphTitle: { + displayName: 'Set title as Open Graph title', + code: async () => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Set page title as open graph title - await currentPage.useTitleAsOpenGraphTitle(true) - const title = await currentPage.getOpenGraphTitle() - console.log(title) + // Set page title as open graph title + await currentPage.useTitleAsOpenGraphTitle(true) + const title = await currentPage.getOpenGraphTitle() + console.log(title) + }, }, - getOpenGraphTitle: async () => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + getOpenGraphTitle: { + displayName: 'Get Open Graph title', + code: async () => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Get Open Graph Title - const openGraphTitle = await currentPage.getOpenGraphTitle() - console.log('Open Graph Title', openGraphTitle) + // Get Open Graph Title + const openGraphTitle = await currentPage.getOpenGraphTitle() + console.log('Open Graph Title', openGraphTitle) + }, }, - setOpenGraphTitle: async (title: string) => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + setOpenGraphTitle: { + displayName: 'Set Open Graph title', + code: async (title: string) => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Set Open Graph Title - await currentPage.setOpenGraphTitle(title) + // Set Open Graph Title + await currentPage.setOpenGraphTitle(title) - // Print results - const openGraphTitle = await currentPage.getOpenGraphTitle() - console.log('Open Graph Title', openGraphTitle) + // Print results + const openGraphTitle = await currentPage.getOpenGraphTitle() + console.log('Open Graph Title', openGraphTitle) + }, }, - checkOpenGraphDescription: async () => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page - - // Check page status - const isOpenGraphDescription = - await currentPage.usesDescriptionAsOpenGraphDescription() + checkOpenGraphDescription: { + displayName: 'Check Open Graph description', + code: async () => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Print page status - if (isOpenGraphDescription) { - console.log('Page uses Page Description as Open Graph Description') - } else { - console.log('This page has a custom Open Graph Description') - } + // Check page status + const isOpenGraphDescription = + await currentPage.usesDescriptionAsOpenGraphDescription() + + // Print page status + if (isOpenGraphDescription) { + console.log('Page uses Page Description as Open Graph Description') + } else { + console.log('This page has a custom Open Graph Description') + } + }, }, - setDescriptionAsOpenGraphDescription: async () => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + setDescriptionAsOpenGraphDescription: { + displayName: 'Set description as Open Graph description', + code: async () => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Set page description open graph description - await currentPage.useDescriptionAsOpenGraphDescription(true) - const description = await currentPage.getOpenGraphDescription() - console.log(description) + // Set page description open graph description + await currentPage.useDescriptionAsOpenGraphDescription(true) + const description = await currentPage.getOpenGraphDescription() + console.log(description) + }, }, - getOpenGraphDescription: async () => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + getOpenGraphDescription: { + displayName: 'Get Open Graph description', + code: async () => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Get Open Graph Description - const openGraphDescription = await currentPage.getOpenGraphDescription() - console.log('Open Graph Description', openGraphDescription) + // Get Open Graph Description + const openGraphDescription = await currentPage.getOpenGraphDescription() + console.log('Open Graph Description', openGraphDescription) + }, }, - setOpenGraphDescription: async (description: string) => { - // Get Current Page - const currentPage = (await webflow.getCurrentPage()) as Page + setOpenGraphDescription: { + displayName: 'Set Open Graph description', + code: async (description: string) => { + // Get Current Page + const currentPage = (await webflow.getCurrentPage()) as Page - // Set Open Graph Description - await currentPage.setOpenGraphDescription(description) + // Set Open Graph Description + await currentPage.setOpenGraphDescription(description) + }, }, }, } diff --git a/src/examples/payments.ts b/src/examples/payments.ts index 37e736e..86c545b 100644 --- a/src/examples/payments.ts +++ b/src/examples/payments.ts @@ -1,12 +1,13 @@ export const Payments = { - subscriptions: { - getAppSubscriptions: async () => { - - const subcriptions = await webflow.getAppSubscriptions() - console.log(`Subscriptions: ${subcriptions}`) - - }, + subscriptions: { + getAppSubscriptions: { + displayName: 'Get app subscriptions', + code: async () => { + const subcriptions = await webflow.getAppSubscriptions() + console.log(`Subscriptions: ${subcriptions}`) + }, }, + }, -} \ No newline at end of file +} diff --git a/src/examples/styles.ts b/src/examples/styles.ts index c5ce903..3583f30 100644 --- a/src/examples/styles.ts +++ b/src/examples/styles.ts @@ -101,386 +101,450 @@ export const getStyleObjectByName = async ( export const Styles = { StyleManagement: { - getAllStyles: async () => { - // Get all Styles - const allStyles = await webflow.getAllStyles() - - // List Styles - if (allStyles.length > 0) { - console.log('List of all styles:') - - allStyles.forEach(async (style, index) => { - // Print style names and ids - console.log( - `${index + 1}. Style Name: ${await style.getName()}, Style ID: ${style.id}`, - ) - }) - } else { - console.log('No styles found in the current context.') - } + getAllStyles: { + displayName: 'Get all styles', + code: async () => { + // Get all Styles + const allStyles = await webflow.getAllStyles() + + // List Styles + if (allStyles.length > 0) { + console.log('List of all styles:') + + allStyles.forEach(async (style, index) => { + // Print style names and ids + console.log( + `${index + 1}. Style Name: ${await style.getName()}, Style ID: ${style.id}`, + ) + }) + } else { + console.log('No styles found in the current context.') + } + }, }, - getStyleByName: async (styleName: string) => { - // Retrieve the style by name - const retrievedStyle = await webflow.getStyleByName(styleName) - - if (retrievedStyle) { - // Get and print properties of the retrieved style - const styleProperties = await retrievedStyle.getProperties() - console.log('Style properties:', styleProperties) - } else { - console.log(`Style ${styleName} not found.`) - } + getStyleByName: { + displayName: 'Get style by name', + code: async (styleName: string) => { + // Retrieve the style by name + const retrievedStyle = await webflow.getStyleByName(styleName) + + if (retrievedStyle) { + // Get and print properties of the retrieved style + const styleProperties = await retrievedStyle.getProperties() + console.log('Style properties:', styleProperties) + } else { + console.log(`Style ${styleName} not found.`) + } + }, }, - getStyleByPath: async (styleParentName: string, styleName: string) => { - // Retrieve the style by parent name and style name - const retrievedStyle = await webflow.getStyleByName([styleParentName, styleName]) - - if (retrievedStyle) { - // Get and print properties of the retrieved style - const styleProperties = await retrievedStyle.getProperties(); - console.log("Style properties:", styleProperties); - } else { - console.log(`Style ${styleName} with parent ${styleParentName} not found.`); - } + getStyleByPath: { + displayName: 'Get style by path', + code: async (styleParentName: string, styleName: string) => { + // Retrieve the style by parent name and style name + const retrievedStyle = await webflow.getStyleByName([styleParentName, styleName]) + + if (retrievedStyle) { + // Get and print properties of the retrieved style + const styleProperties = await retrievedStyle.getProperties(); + console.log("Style properties:", styleProperties); + } else { + console.log(`Style ${styleName} with parent ${styleParentName} not found.`); + } + }, }, - createStyle: async (styleName: string) => { - // Create new style - const newStyle = await webflow.createStyle(styleName) - - // Set properties for the style - newStyle.setProperties({ - 'background-color': 'blue', - 'font-size': '16px', - 'font-weight': 'bold', - }) - - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - if (selectedElement?.styles) { - // Apply style to selected element - await selectedElement.setStyles([newStyle]) - } else { - console.log('No element selected') - } + createStyle: { + displayName: 'Create style', + code: async (styleName: string) => { + // Create new style + const newStyle = await webflow.createStyle(styleName) + + // Set properties for the style + newStyle.setProperties({ + 'background-color': 'blue', + 'font-size': '16px', + 'font-weight': 'bold', + }) + + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() + + if (selectedElement?.styles) { + // Apply style to selected element + await selectedElement.setStyles([newStyle]) + } else { + console.log('No element selected') + } + }, }, - removeStyle: async (styleName: string) => { - // Retrieve the style by name - const retrievedStyle = await webflow.getStyleByName(styleName) - - if (retrievedStyle) { - // Remove Style - await webflow.removeStyle(retrievedStyle) - console.log(`Style: ${styleName} was removed`) - } else { - console.log(`Style ${styleName} not found.`) - } + removeStyle: { + displayName: 'Remove style', + code: async (styleName: string) => { + // Retrieve the style by name + const retrievedStyle = await webflow.getStyleByName(styleName) + + if (retrievedStyle) { + // Remove Style + await webflow.removeStyle(retrievedStyle) + console.log(`Style: ${styleName} was removed`) + } else { + console.log(`Style ${styleName} not found.`) + } + }, }, - createStyleApplySelectedElement: async () => { - // Get selected element - const selectedElement = await webflow.getSelectedElement() - - // Create new style - const newStyle = await webflow.createStyle('My Custom Style') - - // Create a variable - const collection = await webflow.getDefaultVariableCollection() - const webflowBlue = await collection?.createColorVariable( - 'Webflow Blue', - '#146EF5', - ) - - // Create a PropertyMap object - const propertyMap: PropertyMap = { - 'background-color': webflowBlue as ColorVariable, - 'font-size': '16px', - 'font-weight': 'bold', - } - - // Set style properties - await newStyle.setProperties(propertyMap) - - // apply newStyle to element - if (selectedElement?.styles) await selectedElement.setStyles([newStyle]) + createStyleApplySelectedElement: { + displayName: 'Create style apply selected element', + code: async () => { + // Get selected element + const selectedElement = await webflow.getSelectedElement() + + // Create new style + const newStyle = await webflow.createStyle('My Custom Style') + + // Create a variable + const collection = await webflow.getDefaultVariableCollection() + const webflowBlue = await collection?.createColorVariable( + 'Webflow Blue', + '#146EF5', + ) + + // Create a PropertyMap object + const propertyMap: PropertyMap = { + 'background-color': webflowBlue as ColorVariable, + 'font-size': '16px', + 'font-weight': 'bold', + } + + // Set style properties + await newStyle.setProperties(propertyMap) + + // apply newStyle to element + if (selectedElement?.styles) await selectedElement.setStyles([newStyle]) + }, }, - isComboClass: async (style: Style) => { - if (style?.isComboClass()) { - console.log('Style is a combo class') - } else { - console.log('Style is not a combo class') - } + isComboClass: { + displayName: 'Is combo class', + code: async (style: Style) => { + if (style?.isComboClass()) { + console.log('Style is a combo class') + } else { + console.log('Style is not a combo class') + } + }, }, - getParentStyle: async (style: Style) => { - if (await style.isComboClass()) { - const parentStyle = await style.getParent() - - if (parentStyle) { - const parentStyleName = await parentStyle.getName() - const parentProps = await parentStyle.getProperties() - const comboProps = await style.getProperties() - - console.log('Parent Style:', parentStyleName) - console.log('Inherited from parent:', parentProps) - console.log('Overrides in combo class:', comboProps) + getParentStyle: { + displayName: 'Get parent style', + code: async (style: Style) => { + if (await style.isComboClass()) { + const parentStyle = await style.getParent() + + if (parentStyle) { + const parentStyleName = await parentStyle.getName() + const parentProps = await parentStyle.getProperties() + const comboProps = await style.getProperties() + + console.log('Parent Style:', parentStyleName) + console.log('Inherited from parent:', parentProps) + console.log('Overrides in combo class:', comboProps) + } + } else { + console.log('Style is not a combo class - no parent exists') } - } else { - console.log('Style is not a combo class - no parent exists') - } + }, }, }, StyleProperties: { - getStyleProperties: async (style: Style) => { - // Now style is the actual Style object, not wrapped - if (style) { - const properties = await style.getProperties() - console.log(properties) - - // Get properties for a specific variant - const variantProperties = await style.getProperties({ variantId: 'variant-123' }) - console.log('Variant properties:', variantProperties) - } + getStyleProperties: { + displayName: 'Get style properties', + code: async (style: Style) => { + // Now style is the actual Style object, not wrapped + if (style) { + const properties = await style.getProperties() + console.log(properties) + + // Get properties for a specific variant + const variantProperties = await style.getProperties({ variantId: 'variant-123' }) + console.log('Variant properties:', variantProperties) + } + }, }, - setStyleProperties: async () => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - if (selectedElement?.styles) { - // Get Element Styles - const styles = await selectedElement.getStyles() - const primaryStyle = styles?.[0] - - if (primaryStyle) { - const propertyMap = { - 'background-color': 'blue', - 'font-size': '16px', - 'font-weight': 'bold', + setStyleProperties: { + displayName: 'Set style properties', + code: async () => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() + + if (selectedElement?.styles) { + // Get Element Styles + const styles = await selectedElement.getStyles() + const primaryStyle = styles?.[0] + + if (primaryStyle) { + const propertyMap = { + 'background-color': 'blue', + 'font-size': '16px', + 'font-weight': 'bold', + } + + await primaryStyle.setProperties(propertyMap) + + // Set properties for a specific variant + await primaryStyle.setProperties(propertyMap, { variantId: 'variant-123' }) + } else { + console.log('Please choose an element with styles') } - - await primaryStyle.setProperties(propertyMap) - - // Set properties for a specific variant - await primaryStyle.setProperties(propertyMap, { variantId: 'variant-123' }) - } else { - console.log('Please choose an element with styles') } - } + }, }, - getStyleProperty: async (propertyName: StyleProperty) => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - if (selectedElement?.styles) { - // Get Element Styles - const styles = await selectedElement.getStyles() - - // For each style, list values of propertyName - if (styles) { - const selectedPropertyList = await Promise.all( - styles.map(async (style) => { - if (style) { - const styleName = await style.getName() - const property = await style.getProperty(propertyName) - console.log( - `Style Name: ${styleName}, ${propertyName}: ${property}`, - ) - - // Get the same property for a specific variant - const variantProperty = await style.getProperty(propertyName, { variantId: 'variant-123' }) - console.log(`Variant property — ${propertyName}: ${variantProperty}`) - } - }), - ) + getStyleProperty: { + displayName: 'Get style property', + code: async (propertyName: StyleProperty) => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() + + if (selectedElement?.styles) { + // Get Element Styles + const styles = await selectedElement.getStyles() + + // For each style, list values of propertyName + if (styles) { + const selectedPropertyList = await Promise.all( + styles.map(async (style) => { + if (style) { + const styleName = await style.getName() + const property = await style.getProperty(propertyName) + console.log( + `Style Name: ${styleName}, ${propertyName}: ${property}`, + ) + + // Get the same property for a specific variant + const variantProperty = await style.getProperty(propertyName, { variantId: 'variant-123' }) + console.log(`Variant property — ${propertyName}: ${variantProperty}`) + } + }), + ) + } } - } + }, }, - setStyleProperty: async ( - styleProperty: StyleProperty, - propertyValue: string, - ) => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - if (selectedElement?.styles) { - // Get Element Styles - const styles = await selectedElement.getStyles() - const primaryStyle = styles?.[0] - if (primaryStyle) { - await primaryStyle.setProperty(styleProperty, propertyValue) - console.log(primaryStyle.getProperty(styleProperty)) - - // Set the property for a specific variant - await primaryStyle.setProperty(styleProperty, propertyValue, { variantId: 'variant-123' }) + setStyleProperty: { + displayName: 'Set style property', + code: async ( + styleProperty: StyleProperty, + propertyValue: string, + ) => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() + + if (selectedElement?.styles) { + // Get Element Styles + const styles = await selectedElement.getStyles() + const primaryStyle = styles?.[0] + if (primaryStyle) { + await primaryStyle.setProperty(styleProperty, propertyValue) + console.log(primaryStyle.getProperty(styleProperty)) + + // Set the property for a specific variant + await primaryStyle.setProperty(styleProperty, propertyValue, { variantId: 'variant-123' }) + } } - } + }, }, - clearAllStyleProperties: async (styleName: string) => { - // Retrieve the style by name - const retrievedStyle = await webflow.getStyleByName(styleName) - - // Remove all properties from the base style - await retrievedStyle?.removeAllProperties() - - // Remove all properties for a specific variant - await retrievedStyle?.removeAllProperties({ variantId: 'variant-123' }) + clearAllStyleProperties: { + displayName: 'Clear all style properties', + code: async (styleName: string) => { + // Retrieve the style by name + const retrievedStyle = await webflow.getStyleByName(styleName) + + // Remove all properties from the base style + await retrievedStyle?.removeAllProperties() + + // Remove all properties for a specific variant + await retrievedStyle?.removeAllProperties({ variantId: 'variant-123' }) + }, }, - removeSingleStyleProperty: async (property: StyleProperty) => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - if (selectedElement?.styles) { - // Get Element Styles - const styles = await selectedElement.getStyles() - const primaryStyle = styles?.[0] - if (primaryStyle) { - await primaryStyle.removeProperty(property) - - // Remove the property for a specific variant - await primaryStyle.removeProperty(property, { variantId: 'variant-123' }) + removeSingleStyleProperty: { + displayName: 'Remove single style property', + code: async (property: StyleProperty) => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() + + if (selectedElement?.styles) { + // Get Element Styles + const styles = await selectedElement.getStyles() + const primaryStyle = styles?.[0] + if (primaryStyle) { + await primaryStyle.removeProperty(property) + + // Remove the property for a specific variant + await primaryStyle.removeProperty(property, { variantId: 'variant-123' }) + } } - } + }, }, - removeMultipleStyleProperties: async () => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - if (selectedElement?.styles) { - // Get Element Styles - const styles = await selectedElement.getStyles() - const primaryStyle = styles?.[0] - if (primaryStyle) { - const properties: StyleProperty[] = [ - 'background-color', - 'accent-color', - 'font-family', - ] - await primaryStyle.removeProperties(properties) - - // Remove the same properties for a specific variant - await primaryStyle.removeProperties(properties, { variantId: 'variant-123' }) + removeMultipleStyleProperties: { + displayName: 'Remove multiple style properties', + code: async () => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() + + if (selectedElement?.styles) { + // Get Element Styles + const styles = await selectedElement.getStyles() + const primaryStyle = styles?.[0] + if (primaryStyle) { + const properties: StyleProperty[] = [ + 'background-color', + 'accent-color', + 'font-family', + ] + await primaryStyle.removeProperties(properties) + + // Remove the same properties for a specific variant + await primaryStyle.removeProperties(properties, { variantId: 'variant-123' }) + } } - } + }, }, }, VariableModes: { - // Variable Modes - - getVariableModes: async () => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - if (selectedElement?.styles) { - const styles = await selectedElement.getStyles() - if (styles) { - const primaryStyle = styles[0] - const variableModes = await primaryStyle?.getVariableModes() - console.log(variableModes) + getVariableModes: { + displayName: 'Get variable modes', + code: async () => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() + + if (selectedElement?.styles) { + const styles = await selectedElement.getStyles() + if (styles) { + const primaryStyle = styles[0] + const variableModes = await primaryStyle?.getVariableModes() + console.log(variableModes) + } } - } + }, }, - getVariableMode: async (variableCollection: VariableCollection) => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - if (selectedElement?.styles) { - // Get Styles - const styles = await selectedElement.getStyles() - const primaryStyle = styles?.[0] // Get the primary style - - // Get Variable Mode - if (primaryStyle && variableCollection) { - const variableMode = - await primaryStyle.getVariableMode(variableCollection) - const variableModeName = await variableMode?.getName() - console.log(variableModeName) + getVariableMode: { + displayName: 'Get variable mode', + code: async (variableCollection: VariableCollection) => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() + + if (selectedElement?.styles) { + // Get Styles + const styles = await selectedElement.getStyles() + const primaryStyle = styles?.[0] // Get the primary style + + // Get Variable Mode + if (primaryStyle && variableCollection) { + const variableMode = + await primaryStyle.getVariableMode(variableCollection) + const variableModeName = await variableMode?.getName() + console.log(variableModeName) + } } - } + }, }, - setVariableModes: async (selectedStyle: Style) => { - // This function gets variable modes from the style of the currently selected element, then sets them on the style selected in the explorer + setVariableModes: { + displayName: 'Set variable modes', + code: async (selectedStyle: Style) => { + // This function gets variable modes from the style of the currently selected element, then sets them on the style selected in the explorer - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() - if (selectedElement?.styles) { - // Get Styles - const styles = await selectedElement.getStyles() - const primaryStyle = styles?.[0] // Get the primary style - const variableModes = await primaryStyle?.getVariableModes() + if (selectedElement?.styles) { + // Get Styles + const styles = await selectedElement.getStyles() + const primaryStyle = styles?.[0] // Get the primary style + const variableModes = await primaryStyle?.getVariableModes() - // Set Variable Modes on Selected Style - if (variableModes) { - await selectedStyle?.setVariableModes(variableModes) + // Set Variable Modes on Selected Style + if (variableModes) { + await selectedStyle?.setVariableModes(variableModes) + } } - } + }, }, - setVariableMode: async ( - variableCollection: VariableCollection, - variableMode: VariableMode, - ) => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - if (selectedElement?.styles) { - // Get Styles - const styles = await selectedElement.getStyles() - const primaryStyle = styles?.[0] // Get the primary style - - // Set Variable Mode - if (primaryStyle && variableCollection) { - await primaryStyle.setVariableMode(variableCollection, variableMode) - console.log('Variable mode set successfully') + setVariableMode: { + displayName: 'Set variable mode', + code: async ( + variableCollection: VariableCollection, + variableMode: VariableMode, + ) => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() + + if (selectedElement?.styles) { + // Get Styles + const styles = await selectedElement.getStyles() + const primaryStyle = styles?.[0] // Get the primary style + + // Set Variable Mode + if (primaryStyle && variableCollection) { + await primaryStyle.setVariableMode(variableCollection, variableMode) + console.log('Variable mode set successfully') + } } - } + }, }, - removeVariableMode: async (variableCollection: VariableCollection) => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() + removeVariableMode: { + displayName: 'Remove variable mode', + code: async (variableCollection: VariableCollection) => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() - if (selectedElement?.styles) { - // Get Styles - const styles = await selectedElement.getStyles() - const primaryStyle = styles?.[0] // Get the primary style + if (selectedElement?.styles) { + // Get Styles + const styles = await selectedElement.getStyles() + const primaryStyle = styles?.[0] // Get the primary style - await primaryStyle?.removeVariableMode(variableCollection) - } + await primaryStyle?.removeVariableMode(variableCollection) + } + }, }, - removeVariableModes: async (variableCollection: VariableCollection) => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() - - if (selectedElement?.styles) { - // Get Styles - const styles = await selectedElement.getStyles() - const primaryStyle = styles?.[0] // Get the primary style - - // Get Variable Modes - const variableModes = await variableCollection?.getAllVariableModes() - const remove = await primaryStyle?.removeVariableModes(variableModes) - console.log(remove) - } + removeVariableModes: { + displayName: 'Remove variable modes', + code: async (variableCollection: VariableCollection) => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() + + if (selectedElement?.styles) { + // Get Styles + const styles = await selectedElement.getStyles() + const primaryStyle = styles?.[0] // Get the primary style + + // Get Variable Modes + const variableModes = await variableCollection?.getAllVariableModes() + const remove = await primaryStyle?.removeVariableModes(variableModes) + console.log(remove) + } + }, }, - removeAllVariableModes: async () => { - // Get Selected Element - const selectedElement = await webflow.getSelectedElement() + removeAllVariableModes: { + displayName: 'Remove all variable modes', + code: async () => { + // Get Selected Element + const selectedElement = await webflow.getSelectedElement() - if (selectedElement?.styles) { - // Get Styles - const styles = await selectedElement.getStyles() - const primaryStyle = styles?.[0] // Get the primary style + if (selectedElement?.styles) { + // Get Styles + const styles = await selectedElement.getStyles() + const primaryStyle = styles?.[0] // Get the primary style - // Get Variable Modes - const remove = await primaryStyle?.removeAllVariableModes() - console.log(remove) - } + // Get Variable Modes + const remove = await primaryStyle?.removeAllVariableModes() + console.log(remove) + } + }, }, }, } diff --git a/src/examples/utilities.ts b/src/examples/utilities.ts index c9c2f7d..764a500 100644 --- a/src/examples/utilities.ts +++ b/src/examples/utilities.ts @@ -9,271 +9,319 @@ export enum ExtensionSizeEnum { export const Utilities = { // Site Info siteInfo: { - getSiteInfo: async () => { - // Get Site Information - const siteInfo = await webflow.getSiteInfo() - - // Print Site Information - console.log(JSON.stringify(siteInfo, null, 2)) + getSiteInfo: { + displayName: 'Get site info', + code: async () => { + // Get Site Information + const siteInfo = await webflow.getSiteInfo() + + // Print Site Information + console.log(JSON.stringify(siteInfo, null, 2)) + }, }, - getIdToken: async () => { - // Get ID Token - const idToken = await webflow.getIdToken() + getIdToken: { + displayName: 'Get ID token', + code: async () => { + // Get ID Token + const idToken = await webflow.getIdToken() - // Print ID Token - console.log(idToken) + // Print ID Token + console.log(idToken) + }, }, - checkAppMode: async () => { - const capabilities = await webflow.canForAppMode([ - webflow.appModes.canEdit, - webflow.appModes.canDesign, - ]) + checkAppMode: { + displayName: 'Check app mode', + code: async () => { + const capabilities = await webflow.canForAppMode([ + webflow.appModes.canEdit, + webflow.appModes.canDesign, + ]) - console.log(capabilities) + console.log(capabilities) + }, }, - checkAppConnection: async () => { - // Check for current app connection - const appConnection = await webflow.getCurrentAppConnection() - console.log(appConnection) + checkAppConnection: { + displayName: 'Check app connection', + code: async () => { + // Check for current app connection + const appConnection = await webflow.getCurrentAppConnection() + console.log(appConnection) + }, }, - getLaunchContext: async () => { - const context = await webflow.getLaunchContext() - console.log('Launch Context:', context) + getLaunchContext: { + displayName: 'Get launch context', + code: async () => { + const context = await webflow.getLaunchContext() + console.log('Launch Context:', context) - if (context) { - const contextType = context.type - const contextValue = context.value + if (context) { + const contextType = context.type + const contextValue = context.value - console.log('Launch Type:', contextType) - console.log('Launch Value:', contextValue) + console.log('Launch Type:', contextType) + console.log('Launch Value:', contextValue) - // Notify user of launch context - await webflow.notify({ - type: 'Info', - message: `App launched via ${contextType}${contextValue ? ` with ${JSON.stringify(contextValue)}` : ''}`, - }) - } else { - console.log('No specific launch context') - } + // Notify user of launch context + await webflow.notify({ + type: 'Info', + message: `App launched via ${contextType}${contextValue ? ` with ${JSON.stringify(contextValue)}` : ''}`, + }) + } else { + console.log('No specific launch context') + } + }, }, }, // Display & UI displayAndUI: { - setExtensionSize: async (extensionSizeEnum: ExtensionSizeEnum) => { - // Set the extension UI size to "default", "comfortable", or "large" - await webflow.setExtensionSize(extensionSizeEnum.id) - - console.log(`Extension UI size set to: ${extensionSizeEnum.id}`) - }, - - displayCurrentMediaQuery: async () => { - const breakpointId = await webflow.getMediaQuery() - - switch (breakpointId) { - case 'xxl': - console.log( - 'The current view is for very large screens or high-resolution monitors.', - ) - break - case 'xl': - console.log('The current view is suitable for large desktop monitors.') - break - case 'large': - console.log('The current view fits standard desktop monitors.') - break - case 'main': - console.log( - 'The current view is suitable for smaller desktops or large tablets.', - ) - break - case 'medium': - console.log( - 'The current view is suitable for tablets and some large phones.', - ) - break - case 'small': - console.log('The current view is designed for larger mobile devices.') - break - case 'tiny': - console.log('The current view is for the smallest mobile devices.') - break - } + setExtensionSize: { + displayName: 'Set extension size', + code: async (extensionSizeEnum: ExtensionSizeEnum) => { + // Set the extension UI size to "default", "comfortable", or "large" + await webflow.setExtensionSize(extensionSizeEnum.id) + + console.log(`Extension UI size set to: ${extensionSizeEnum.id}`) + }, }, - getPseudoMode: async () => { - // Select a state in the designer to see the pseudo mode in the console - const pseudoMode = await webflow.getPseudoMode() - console.log('Pseudo Mode:', pseudoMode) + displayCurrentMediaQuery: { + displayName: 'Display current media query', + code: async () => { + const breakpointId = await webflow.getMediaQuery() + + switch (breakpointId) { + case 'xxl': + console.log( + 'The current view is for very large screens or high-resolution monitors.', + ) + break + case 'xl': + console.log('The current view is suitable for large desktop monitors.') + break + case 'large': + console.log('The current view fits standard desktop monitors.') + break + case 'main': + console.log( + 'The current view is suitable for smaller desktops or large tablets.', + ) + break + case 'medium': + console.log( + 'The current view is suitable for tablets and some large phones.', + ) + break + case 'small': + console.log('The current view is designed for larger mobile devices.') + break + case 'tiny': + console.log('The current view is for the smallest mobile devices.') + break + } + }, }, - notifyUser: async () => { - // General notification - await webflow.notify({ type: 'Info', message: 'Great work!' }) - - // Error notification - await webflow.notify({ - type: 'Error', - message: 'Something went wrong, try again!', - }) - - // Success notification - await webflow.notify({ - type: 'Success', - message: 'Successfully did something!', - }) + getPseudoMode: { + displayName: 'Get pseudo mode', + code: async () => { + // Select a state in the designer to see the pseudo mode in the console + const pseudoMode = await webflow.getPseudoMode() + console.log('Pseudo Mode:', pseudoMode) + }, }, - getElementSnapshot: async () => { - // Get the currently selected element - const selectedElement = await webflow.getSelectedElement() + notifyUser: { + displayName: 'Notify user', + code: async () => { + // General notification + await webflow.notify({ type: 'Info', message: 'Great work!' }) - if (selectedElement) { - // Capture a screenshot of the element - console.log('Getting screenshot of element...') - const screenshot = await webflow.getElementSnapshot(selectedElement) + // Error notification + await webflow.notify({ + type: 'Error', + message: 'Something went wrong, try again!', + }) - if (screenshot) { - console.log('Screenshot captured successfully!') - console.log('Base64 string:', screenshot.substring(0, 100) + '...') + // Success notification + await webflow.notify({ + type: 'Success', + message: 'Successfully did something!', + }) + }, + }, - console.log( - '%c ', - `font-size:400px; background:url(${screenshot}) no-repeat; background-size: contain;`, - ) + getElementSnapshot: { + displayName: 'Get element snapshot', + code: async () => { + // Get the currently selected element + const selectedElement = await webflow.getSelectedElement() + + if (selectedElement) { + // Capture a screenshot of the element + console.log('Getting screenshot of element...') + const screenshot = await webflow.getElementSnapshot(selectedElement) + + if (screenshot) { + console.log('Screenshot captured successfully!') + console.log('Base64 string:', screenshot.substring(0, 100) + '...') + + console.log( + '%c ', + `font-size:400px; background:url(${screenshot}) no-repeat; background-size: contain;`, + ) + } else { + console.log('Failed to capture screenshot') + } } else { - console.log('Failed to capture screenshot') + console.log('No element selected. Please select an element first.') } - } else { - console.log('No element selected. Please select an element first.') - } + }, }, }, // Subscriptions subscriptions: { - subscribeSelect: async () => { - // Subscribe to changes in the selected element - const selectedElementCallback = (element: AnyElement | null) => { - if (element) { - console.log('Selected Element:', element) - } else { - console.log('No element is currently selected.') + subscribeSelect: { + displayName: 'Subscribe select', + code: async () => { + // Subscribe to changes in the selected element + const selectedElementCallback = (element: AnyElement | null) => { + if (element) { + console.log('Selected Element:', element) + } else { + console.log('No element is currently selected.') + } } - } - const unsubscribeSelectedElement = webflow.subscribe( - 'selectedelement', - selectedElementCallback, - ) + const unsubscribeSelectedElement = webflow.subscribe( + 'selectedelement', + selectedElementCallback, + ) + }, }, - subscribeBreakpoint: async () => { - const unsubscribeMediaQuery = webflow.subscribe( - 'mediaquery', - (breakpoint) => { - switch (breakpoint) { - case 'xxl': - console.log( - 'The current view is for very large screens or high-resolution monitors.', - ) - break - case 'xl': - console.log( - 'The current view is suitable for large desktop monitors.', - ) - break - case 'large': - console.log('The current view fits standard desktop monitors.') - break - case 'main': - console.log( - 'The current view is suitable for smaller desktops or large tablets.', - ) - break - case 'medium': - console.log( - 'The current view is suitable for tablets and some large phones.', - ) - break - case 'small': - console.log( - 'The current view is designed for larger mobile devices.', - ) - break - case 'tiny': - console.log('The current view is for the smallest mobile devices.') - break - default: - console.log('Unknown breakpoint:', breakpoint) - } - }, - ) + subscribeBreakpoint: { + displayName: 'Subscribe breakpoint', + code: async () => { + const unsubscribeMediaQuery = webflow.subscribe( + 'mediaquery', + (breakpoint) => { + switch (breakpoint) { + case 'xxl': + console.log( + 'The current view is for very large screens or high-resolution monitors.', + ) + break + case 'xl': + console.log( + 'The current view is suitable for large desktop monitors.', + ) + break + case 'large': + console.log('The current view fits standard desktop monitors.') + break + case 'main': + console.log( + 'The current view is suitable for smaller desktops or large tablets.', + ) + break + case 'medium': + console.log( + 'The current view is suitable for tablets and some large phones.', + ) + break + case 'small': + console.log( + 'The current view is designed for larger mobile devices.', + ) + break + case 'tiny': + console.log('The current view is for the smallest mobile devices.') + break + default: + console.log('Unknown breakpoint:', breakpoint) + } + }, + ) + }, }, - subscribePageChange: async () => { - // Subscribe to changes in the selected page - const selectedPageCallback = async (page: Page | null) => { - if (page) { - let pageName = await page.getName() - let pageSlug = await page.getSlug() - let pageParent = await page.getParent() - let searchDescription = await page.getSearchDescription() - - console.log(`Page Name: ${pageName}`) - console.log(`Page Slug: ${pageSlug}`) - console.log(`Page Description: ${searchDescription}`) - } else { - console.log('No element is currently selected.') + subscribePageChange: { + displayName: 'Subscribe page change', + code: async () => { + // Subscribe to changes in the selected page + const selectedPageCallback = async (page: Page | null) => { + if (page) { + let pageName = await page.getName() + let pageSlug = await page.getSlug() + let pageParent = await page.getParent() + let searchDescription = await page.getSearchDescription() + + console.log(`Page Name: ${pageName}`) + console.log(`Page Slug: ${pageSlug}`) + console.log(`Page Description: ${searchDescription}`) + } else { + console.log('No element is currently selected.') + } } - } - const unsubscribeSelectedElement = webflow.subscribe( - 'currentpage', - selectedPageCallback, - ) + const unsubscribeSelectedElement = webflow.subscribe( + 'currentpage', + selectedPageCallback, + ) + }, }, - subscribeAppModes: async () => { - // Subscribe to changes in the selected page - const checkAppModes = async () => { - const capabilities = await webflow.canForAppMode( - Object.values(webflow.appModes), - ) - console.log(capabilities) - } + subscribeAppModes: { + displayName: 'Subscribe app modes', + code: async () => { + // Subscribe to changes in the selected page + const checkAppModes = async () => { + const capabilities = await webflow.canForAppMode( + Object.values(webflow.appModes), + ) + console.log(capabilities) + } - const unsubscribeSelectedElement = webflow.subscribe( - 'currentappmode', - checkAppModes, - ) + const unsubscribeSelectedElement = webflow.subscribe( + 'currentappmode', + checkAppModes, + ) + }, }, - subscribePseudoMode: async () => { - // Subscribe to changes in the pseudo mode - const pseudoModeCallback = (pseudoMode: PseudoStateKey | null) => { - console.log('Pseudo Mode:', pseudoMode) - } + subscribePseudoMode: { + displayName: 'Subscribe pseudo mode', + code: async () => { + // Subscribe to changes in the pseudo mode + const pseudoModeCallback = (pseudoMode: PseudoStateKey | null) => { + console.log('Pseudo Mode:', pseudoMode) + } - const unsubscribePseudoMode = webflow.subscribe( - 'pseudomode', - pseudoModeCallback, - ) + const unsubscribePseudoMode = webflow.subscribe( + 'pseudomode', + pseudoModeCallback, + ) + }, }, - subscribeSelectedVariant: async () => { - // Subscribe to variant selection changes on the component canvas - const unsubscribe = webflow.subscribe('selectedvariant', (variant) => { - console.log('Selected variant:', variant.name) - console.log('Variant ID:', variant.id) - }) + subscribeSelectedVariant: { + displayName: 'Subscribe selected variant', + code: async () => { + // Subscribe to variant selection changes on the component canvas + const unsubscribe = webflow.subscribe('selectedvariant', (variant) => { + console.log('Selected variant:', variant.name) + console.log('Variant ID:', variant.id) + }) - // Stop listening after 10 seconds - setTimeout(unsubscribe, 10000) + // Stop listening after 10 seconds + setTimeout(unsubscribe, 10000) + }, }, }, } diff --git a/src/examples/variables.ts b/src/examples/variables.ts index bac3450..1eadc91 100644 --- a/src/examples/variables.ts +++ b/src/examples/variables.ts @@ -24,17 +24,12 @@ let _variableCollectionsObjectSelectorPromise: Promise< export const getVariableCollectionsObjectSelector = async (): Promise< ObjectSelector > => { - // If we already have a promise in progress, return it to avoid multiple simultaneous calls if (_variableCollectionsObjectSelectorPromise) { return _variableCollectionsObjectSelectorPromise } - - // If we already have the selector, return it if (_variableCollectionsObjectSelector) { return _variableCollectionsObjectSelector } - - // Create a new promise for the selector creation _variableCollectionsObjectSelectorPromise = (async () => { const collections = await webflow.getAllVariableCollections() _variableCollectionsObjectSelector = await createObjectSelector( @@ -45,10 +40,9 @@ export const getVariableCollectionsObjectSelector = async (): Promise< object: collection, }), ) - _variableCollectionsObjectSelectorPromise = null // Clear the promise + _variableCollectionsObjectSelectorPromise = null return _variableCollectionsObjectSelector })() - return _variableCollectionsObjectSelectorPromise } @@ -59,22 +53,14 @@ let _variablesObjectSelectorPromise: Promise> | null = null export const getVariablesObjectSelector = async (): Promise< ObjectSelector > => { - // If we already have a promise in progress, return it to avoid multiple simultaneous calls if (_variablesObjectSelectorPromise) { return _variablesObjectSelectorPromise } - - // If we already have the selector, return it if (_variablesObjectSelector) { return _variablesObjectSelector } - - // Create a new promise for the selector creation _variablesObjectSelectorPromise = (async () => { - // Get all collections first const collections = await webflow.getAllVariableCollections() - - // Get all variables from all collections const allVariables = [] for (const collection of collections) { const variables = await collection.getAllVariables() @@ -86,7 +72,6 @@ export const getVariablesObjectSelector = async (): Promise< }) } } - _variablesObjectSelector = await createObjectSelector( allVariables, async (variable) => ({ @@ -95,10 +80,9 @@ export const getVariablesObjectSelector = async (): Promise< object: variable, }), ) - _variablesObjectSelectorPromise = null // Clear the promise + _variablesObjectSelectorPromise = null return _variablesObjectSelector })() - return _variablesObjectSelectorPromise } @@ -173,7 +157,6 @@ export const getVariablesEnum = async ( if (!collection) { throw new Error(`Collection with ID ${collectionInfo.id} not found`) } - const variables = await collection.getAllVariables() return createDynamicEnumMap(variables, async (variable) => ({ id: variable.id, @@ -208,7 +191,6 @@ export const createVariableModeObjectSelector = async ( if (!collection) { throw new Error(`Collection with ID ${collectionId} not found`) } - const variableModes = await collection.getAllVariableModes() return createObjectSelector(variableModes, async (mode) => ({ id: mode.id, @@ -224,12 +206,9 @@ const variableModeSelectorsCache = new Map>() export const getVariableModeObjectSelector = async ( collectionId: string, ): Promise> => { - // Check if we already have a selector for this collection if (variableModeSelectorsCache.has(collectionId)) { return variableModeSelectorsCache.get(collectionId)! } - - // Create new selector const selector = await createVariableModeObjectSelector(collectionId) variableModeSelectorsCache.set(collectionId, selector) return selector @@ -261,449 +240,537 @@ export const getVariableByName = async ( export const Variables = { // Collection Management collectionManagement: { - getAllVariableCollections: async () => { - // Get All Variable Collections - const variableCollections = await webflow.getAllVariableCollections() - console.log('All Variable Collections:') - console.log(variableCollections) - }, - - createVariableCollection: async (name: string) => { - // Create a new variable collection - const newVariableCollection = await webflow.createVariableCollection(name) - console.log('New Variable Collection:') - console.log(newVariableCollection) - }, - - getVariableCollectionById: async (id: string) => { - // Get Variable Collection by ID - const variableCollection = await webflow.getVariableCollectionById(id) - console.log('Variable Collection:') - console.log(variableCollection) - }, - - getDefaultVariableCollection: async () => { - // Get Collection - const defaultVariableCollection = - await webflow.getDefaultVariableCollection() - console.log('Default Variable Collection:') - console.log(defaultVariableCollection) - - // Fetch all variables within the default collection - const variables = await defaultVariableCollection?.getAllVariables() - console.log('Variables in Default Collection:') - console.log(variables) - }, - - removeVariableCollection: async (id: string) => { - // Remove Variable Collection - const removedVariableCollection = - await webflow.removeVariableCollection(id) - console.log(removedVariableCollection) - }, - - getCollectionName: async () => { - // Get Collection - const defaultVariableCollection = - await webflow.getDefaultVariableCollection() - - // Get Collection Name - const collectionName = await defaultVariableCollection?.getName() - console.log(collectionName) - }, - - getCollectionAndVariables: async ( - variableCollection: VariableCollection, - ) => { - if (variableCollection) { - // Print Collection ID - console.log('Default Variable Collection ID:', variableCollection.id) + getAllVariableCollections: { + displayName: 'Get all variable collections', + code: async () => { + // Get All Variable Collections + const variableCollections = await webflow.getAllVariableCollections() + console.log('All Variable Collections:') + console.log(variableCollections) + }, + }, + + createVariableCollection: { + displayName: 'Create variable collection', + code: async (name: string) => { + // Create a new variable collection + const newVariableCollection = await webflow.createVariableCollection(name) + console.log('New Variable Collection:') + console.log(newVariableCollection) + }, + }, + + getVariableCollectionById: { + displayName: 'Get variable collection by ID', + code: async (id: string) => { + // Get Variable Collection by ID + const variableCollection = await webflow.getVariableCollectionById(id) + console.log('Variable Collection:') + console.log(variableCollection) + }, + }, + + getDefaultVariableCollection: { + displayName: 'Get default variable collection', + code: async () => { + // Get Collection + const defaultVariableCollection = + await webflow.getDefaultVariableCollection() + console.log('Default Variable Collection:') + console.log(defaultVariableCollection) // Fetch all variables within the default collection - const variables = await variableCollection.getAllVariables() - - if (variables.length > 0) { - const collectionName = await variableCollection.getName() - console.log(`List of Variables in ${collectionName}:`) - - // Print variable details - for (var i in variables) { - console.log( - `${i}. Variable Name: ${await variables[i].getName()}, Variable ID: ${variables[i].id}`, - ) + const variables = await defaultVariableCollection?.getAllVariables() + console.log('Variables in Default Collection:') + console.log(variables) + }, + }, + + removeVariableCollection: { + displayName: 'Remove variable collection', + code: async (id: string) => { + // Remove Variable Collection + const removedVariableCollection = + await webflow.removeVariableCollection(id) + console.log(removedVariableCollection) + }, + }, + + getCollectionName: { + displayName: 'Get collection name', + code: async () => { + // Get Collection + const defaultVariableCollection = + await webflow.getDefaultVariableCollection() + + // Get Collection Name + const collectionName = await defaultVariableCollection?.getName() + console.log(collectionName) + }, + }, + + getCollectionAndVariables: { + displayName: 'Get collection and variables', + code: async ( + variableCollection: VariableCollection, + ) => { + if (variableCollection) { + // Print Collection ID + console.log('Default Variable Collection ID:', variableCollection.id) + + // Fetch all variables within the default collection + const variables = await variableCollection.getAllVariables() + + if (variables.length > 0) { + const collectionName = await variableCollection.getName() + console.log(`List of Variables in ${collectionName}:`) + + // Print variable details + for (var i in variables) { + console.log( + `${i}. Variable Name: ${await variables[i].getName()}, Variable ID: ${variables[i].id}`, + ) + } + } else { + console.log('No variables found in the default collection.') } } else { - console.log('No variables found in the default collection.') + console.log('Default Variable Collection not found.') } - } else { - console.log('Default Variable Collection not found.') - } + }, }, }, // Variable Creation variableCreation: { - createColorVariable: async ( - variableCollection: VariableCollection, - color: string, - ) => { - // Get Collection - const collection = variableCollection - - // Create Color Variable with a HEX Code - const myColorVariable = await collection?.createColorVariable( - 'primary', - color, - ) - console.log(myColorVariable) + createColorVariable: { + displayName: 'Create color variable', + code: async ( + variableCollection: VariableCollection, + color: string, + ) => { + // Get Collection + const collection = variableCollection + + // Create Color Variable with a HEX Code + const myColorVariable = await collection?.createColorVariable( + 'primary', + color, + ) + console.log(myColorVariable) + }, }, - createCustomColorVariable: async () => { - // Get Collection - const collection = await webflow.getDefaultVariableCollection() + createCustomColorVariable: { + displayName: 'Create custom color variable', + code: async () => { + // Get Collection + const collection = await webflow.getDefaultVariableCollection() - // Create Color Variable - const webflowBlue = await collection?.createColorVariable( - 'blue-500', - '#146EF5', - ) + // Create Color Variable + const webflowBlue = await collection?.createColorVariable( + 'blue-500', + '#146EF5', + ) - // Get the binding to the webflowBlue variable - const webflowBlueBinding = (await webflowBlue?.getBinding()) as string + // Get the binding to the webflowBlue variable + const webflowBlueBinding = (await webflowBlue?.getBinding()) as string - // Function to create a string that uses the binding and CSS color-mix function - const colorMix = (binding: string, color: string, opacity: number) => - `color-mix(in srgb, ${binding} , ${color} ${opacity}%)` + // Function to create a string that uses the binding and CSS color-mix function + const colorMix = (binding: string, color: string, opacity: number) => + `color-mix(in srgb, ${binding} , ${color} ${opacity}%)` - // Create a color variable that uses a CSS function - const webflowBlue400 = await collection?.createColorVariable('blue-400', { - type: 'custom', - value: colorMix(webflowBlueBinding, '#fff', 60), - }) - console.log(webflowBlue400) + // Create a color variable that uses a CSS function + const webflowBlue400 = await collection?.createColorVariable('blue-400', { + type: 'custom', + value: colorMix(webflowBlueBinding, '#fff', 60), + }) + console.log(webflowBlue400) + }, }, - createSizeVariable: async () => { - // Get Collection - const collection = await webflow.getDefaultVariableCollection() + createSizeVariable: { + displayName: 'Create size variable', + code: async () => { + // Get Collection + const collection = await webflow.getDefaultVariableCollection() - // Create Size Variable with a Size Value - const mySizeVariable = await collection?.createSizeVariable( - 'Default Padding', - { unit: 'px', value: 50 }, - ) - console.log(mySizeVariable) + // Create Size Variable with a Size Value + const mySizeVariable = await collection?.createSizeVariable( + 'Default Padding', + { unit: 'px', value: 50 }, + ) + console.log(mySizeVariable) + }, }, - createCustomSizeVariable: async () => { - // Get Collection - const collection = await webflow.getDefaultVariableCollection() + createCustomSizeVariable: { + displayName: 'Create custom size variable', + code: async () => { + // Get Collection + const collection = await webflow.getDefaultVariableCollection() + }, }, - createFontFamilyVariable: async () => { - // Get Collection - const collection = await webflow.getDefaultVariableCollection() + createFontFamilyVariable: { + displayName: 'Create font family variable', + code: async () => { + // Get Collection + const collection = await webflow.getDefaultVariableCollection() - // Create Font Family Variable with a Font Family Name - const myFontFamilyVariable = await collection?.createFontFamilyVariable( - 'Default Font', - 'Inter', - ) - console.log(myFontFamilyVariable) + // Create Font Family Variable with a Font Family Name + const myFontFamilyVariable = await collection?.createFontFamilyVariable( + 'Default Font', + 'Inter', + ) + console.log(myFontFamilyVariable) + }, }, - createNumberVariable: async (name: string, number: number) => { - // Get Collection - const collection = await webflow.getDefaultVariableCollection() + createNumberVariable: { + displayName: 'Create number variable', + code: async (name: string, number: number) => { + // Get Collection + const collection = await webflow.getDefaultVariableCollection() - // Create Number Variable with a Number Value - const myNumberVariable = await collection?.createNumberVariable( - name, - number, - ) - console.log(myNumberVariable) + // Create Number Variable with a Number Value + const myNumberVariable = await collection?.createNumberVariable( + name, + number, + ) + console.log(myNumberVariable) + }, }, - createPercentageVariable: async (percentage: number) => { - // Get Collection - const collection = await webflow.getDefaultVariableCollection() + createPercentageVariable: { + displayName: 'Create percentage variable', + code: async (percentage: number) => { + // Get Collection + const collection = await webflow.getDefaultVariableCollection() - // Create Percentage Variable with a Percentage Value - const myPercentageVariable = await collection?.createPercentageVariable( - 'My Percentage', - percentage, - ) - console.log(myPercentageVariable) + // Create Percentage Variable with a Percentage Value + const myPercentageVariable = await collection?.createPercentageVariable( + 'My Percentage', + percentage, + ) + console.log(myPercentageVariable) + }, }, }, // Variable Management variableManagement: { - getAllVariables: async (variableCollection: VariableCollection) => { - try { - const variables = await variableCollection?.getAllVariables() - if (!variables) { - console.log('No variables in this collection.') - return - } - - const variablePromises = variables.map(async (variable) => { - const name = await variable.getName() - const value = await variable.get({ customValues: true }) - return { - id: variable.id, - name: name, - type: variable.type, - value: value, + getAllVariables: { + displayName: 'Get all variables', + code: async (variableCollection: VariableCollection) => { + try { + const variables = await variableCollection?.getAllVariables() + if (!variables) { + console.log('No variables in this collection.') + return } - }) - - const variableArray = await Promise.all(variablePromises) - console.log('All Variables:', variableArray) - } catch (error) { - console.error('Failed to get all variables:', error) - } - }, - - getVariableById: async ( - variableCollection: VariableCollection, - id: string, - ) => { - // Get Collection - const collection = variableCollection - - // Get variable by ID - const variableById = await collection?.getVariable(id) - console.log(variableById) - }, - - getVariableByName: async ( - variableCollection: VariableCollection, - name: string, - ) => { - // Get Collection - const collection = variableCollection - - // Get Variable by Name - const variableByName = (await collection?.getVariableByName( - name, - )) as ColorVariable - console.log(variableByName) - }, - - editVariable: async ( - variableCollection: VariableCollection, - variableName: string, - newName: string, - ) => { - // Get Collection - const collection = variableCollection - - if (collection) { - // Get variable and reset name - const variable = await collection.getVariableByName(variableName) - console.log(variable) - await variable?.setName(newName) - } - }, - getVariableValue: async ( - collection: VariableCollectionInfo, - variable: VariableInfo, - ) => { - // Get selected collection and variable info - const selCollection = await webflow.getVariableCollectionById( - collection.id, - ) - const selVariable = await selCollection?.getVariable(variable.id) - - // Get variable value, add option to return variables with custom values - let value = await selVariable?.get({ customValues: true }) - - // Log the raw value and get the variable's type - console.log(`Raw value from .get(): ${JSON.stringify(value)}`) - let type = selVariable?.type - - // The `get()` method can return different types of values. - // For simple variables, it's a primitive. (Number, Percentage, Color, etc.) - // Variable references return an object with the referenced variable's ID. - // Custom variables return an object. - - // A variable reference (alias) is returned as an object with the referenced variable's ID: { id: '...' } - if (value && typeof value === 'object' && 'id' in value) { - const referencedVariable = await selCollection?.getVariable(value.id) - const referencedName = await referencedVariable?.getName() - value = `Alias to: ${referencedName}` - type = `Referenced ${type}` - } - - // A custom value is returned as an object: { type: 'custom', value: '...' } - // Extract the inner value for display. - if ( - value && - typeof value === 'object' && - 'type' in value && - value.type === 'custom' - ) { - value = value.value - type = `Custom ${type}` - } - - // Output the variable type and value - console.log(`Variable Type: ${type}`) - console.log(`Variable Value: ${value}`) - - return { value, type } - }, - getVariableBinding: async ( - variableCollection: VariableCollection, - variable: string, - ) => { - // Get Variable - const myVariable = await variableCollection?.getVariableByName(variable) - const binding = await myVariable?.getBinding() - - // Output the binding - console.log(binding) - }, - - getVariableCSSName: async ( - variableCollection: VariableCollection, - variable: string, - ) => { - // Get Variable - const myVariable = await variableCollection?.getVariableByName(variable) - const cssName = await myVariable?.getCSSName() - console.log(cssName) - }, + const variablePromises = variables.map(async (variable) => { + const name = await variable.getName() + const value = await variable.get({ customValues: true }) + return { + id: variable.id, + name: name, + type: variable.type, + value: value, + } + }) + + const variableArray = await Promise.all(variablePromises) + console.log('All Variables:', variableArray) + } catch (error) { + console.error('Failed to get all variables:', error) + } + }, + }, + + getVariableById: { + displayName: 'Get variable by ID', + code: async ( + variableCollection: VariableCollection, + id: string, + ) => { + // Get Collection + const collection = variableCollection + + // Get variable by ID + const variableById = await collection?.getVariable(id) + console.log(variableById) + }, + }, + + getVariableByName: { + displayName: 'Get variable by name', + code: async ( + variableCollection: VariableCollection, + name: string, + ) => { + // Get Collection + const collection = variableCollection + + // Get Variable by Name + const variableByName = (await collection?.getVariableByName( + name, + )) as ColorVariable + console.log(variableByName) + }, + }, + + editVariable: { + displayName: 'Edit variable', + code: async ( + variableCollection: VariableCollection, + variableName: string, + newName: string, + ) => { + // Get Collection + const collection = variableCollection + + if (collection) { + // Get variable and reset name + const variable = await collection.getVariableByName(variableName) + console.log(variable) + await variable?.setName(newName) + } + }, + }, + + getVariableValue: { + displayName: 'Get variable value', + code: async ( + collection: VariableCollectionInfo, + variable: VariableInfo, + ) => { + // Get selected collection and variable info + const selCollection = await webflow.getVariableCollectionById( + collection.id, + ) + const selVariable = await selCollection?.getVariable(variable.id) - setVariableValue: async (name: string) => { - // Get Collection - const collection = await webflow.getDefaultVariableCollection() + // Get variable value, add option to return variables with custom values + let value = await selVariable?.get({ customValues: true }) - // Get Variable - const variable = await collection?.getVariableByName(name) + // Log the raw value and get the variable's type + console.log(`Raw value from .get(): ${JSON.stringify(value)}`) + let type = selVariable?.type - // Check Variable type and set color - if (variable?.type === 'Color') await variable.set('#fffcc11') - }, + // A variable reference (alias) is returned as an object with the referenced variable's ID: { id: '...' } + if (value && typeof value === 'object' && 'id' in value) { + const referencedVariable = await selCollection?.getVariable(value.id) + const referencedName = await referencedVariable?.getName() + value = `Alias to: ${referencedName}` + type = `Referenced ${type}` + } - setVariableValueWithMode: async () => { - // Get the default variable collection - const collection = await webflow.getDefaultVariableCollection() + // A custom value is returned as an object: { type: 'custom', value: '...' } + if ( + value && + typeof value === 'object' && + 'type' in value && + value.type === 'custom' + ) { + value = value.value + type = `Custom ${type}` + } - // Create variable for the collection with a default value - const colorVariable = await collection?.createColorVariable( - 'Body Text', - '#ccc', - ) + // Output the variable type and value + console.log(`Variable Type: ${type}`) + console.log(`Variable Value: ${value}`) + + return { value, type } + }, + }, + + getVariableBinding: { + displayName: 'Get variable binding', + code: async ( + variableCollection: VariableCollection, + variable: string, + ) => { + // Get Variable + const myVariable = await variableCollection?.getVariableByName(variable) + const binding = await myVariable?.getBinding() + + // Output the binding + console.log(binding) + }, + }, + + getVariableCSSName: { + displayName: 'Get variable CSS name', + code: async ( + variableCollection: VariableCollection, + variable: string, + ) => { + // Get Variable + const myVariable = await variableCollection?.getVariableByName(variable) + const cssName = await myVariable?.getCSSName() + console.log(cssName) + }, + }, + + setVariableValue: { + displayName: 'Set variable value', + code: async (name: string) => { + // Get Collection + const collection = await webflow.getDefaultVariableCollection() + + // Get Variable + const variable = await collection?.getVariableByName(name) + + // Check Variable type and set color + if (variable?.type === 'Color') await variable.set('#fffcc11') + }, + }, + + setVariableValueWithMode: { + displayName: 'Set variable value with mode', + code: async () => { + // Get the default variable collection + const collection = await webflow.getDefaultVariableCollection() + + // Create variable for the collection with a default value + const colorVariable = await collection?.createColorVariable( + 'Body Text', + '#ccc', + ) - // Create a variable mode - const variableMode = await collection?.createVariableMode('Dark Mode') + // Create a variable mode + const variableMode = await collection?.createVariableMode('Dark Mode') - // Set a mode-specific value on the variables - await colorVariable?.set('#FFF', { mode: variableMode }) + // Set a mode-specific value on the variables + await colorVariable?.set('#FFF', { mode: variableMode }) + }, }, - applyVariableToStyle: async (styleName: string, variableName: string) => { - // Get collection - const collection = await webflow.getDefaultVariableCollection() + applyVariableToStyle: { + displayName: 'Apply variable to style', + code: async (styleName: string, variableName: string) => { + // Get collection + const collection = await webflow.getDefaultVariableCollection() - // Get Style and desired variable - const style = await webflow.getStyleByName(styleName) - const variable = await collection?.getVariable(variableName) + // Get Style and desired variable + const style = await webflow.getStyleByName(styleName) + const variable = await collection?.getVariable(variableName) - // Check variable type and set property - if (variable?.type === 'Size') - await style?.setProperties({ 'font-size': variable }) + // Check variable type and set property + if (variable?.type === 'Size') + await style?.setProperties({ 'font-size': variable }) + }, }, - getVariableAlias: async () => { - // Get Collection - const collection = await webflow.getDefaultVariableCollection() - - // Create first variable - const firstVariable = await collection?.createColorVariable( - 'Default Color', - 'red', - ) + getVariableAlias: { + displayName: 'Get variable alias', + code: async () => { + // Get Collection + const collection = await webflow.getDefaultVariableCollection() - if (firstVariable) { - // Create second variable as an alias of the first - const secondVariable = await collection?.createColorVariable( - 'Aliased Variable', - firstVariable, + // Create first variable + const firstVariable = await collection?.createColorVariable( + 'Default Color', + 'red', ) - const alias = await secondVariable?.get() - // This will output the ID of the aliased variable - console.log(alias) - } + if (firstVariable) { + // Create second variable as an alias of the first + const secondVariable = await collection?.createColorVariable( + 'Aliased Variable', + firstVariable, + ) + + const alias = await secondVariable?.get() + // This will output the ID of the aliased variable + console.log(alias) + } + }, }, - removeVariable: async () => { - // Get collection - const collection = await webflow.getDefaultVariableCollection() + removeVariable: { + displayName: 'Remove variable', + code: async () => { + // Get collection + const collection = await webflow.getDefaultVariableCollection() - // Get variable by name - const variable = await collection?.getVariable('id-123') + // Get variable by name + const variable = await collection?.getVariable('id-123') - // Delete variable - await variable?.remove() + // Delete variable + await variable?.remove() + }, }, }, + // Variable Modes variableModes: { - getAllVariableModes: async (variableCollection: VariableCollection) => { - // Get All Variable Modes - const variableModes = await variableCollection?.getAllVariableModes() - console.log(variableModes) - }, - - getVariableModeById: async ( - variableCollection: VariableCollection, - modeId: string, - ) => { - // Get Collection - const collection = variableCollection - - // Get Variable Mode by ID - const variableMode = await collection?.getVariableModeById(modeId) - console.log(variableMode) - }, - getVariableModeByName: async ( - variableCollection: VariableCollection, - modeName: string, - ) => { - // Get Collection - const collection = variableCollection - - // Get Variable Mode by Name - const variableMode = await collection?.getVariableModeByName(modeName) - console.log(variableMode) - }, - - // Create Variable Mode - createVariableMode: async ( - variableCollection: VariableCollection, - modeName: string, - ) => { - // Get Collection - const collection = variableCollection - - // Create Variable Mode - const variableMode = await collection?.createVariableMode(modeName) - const newVariableMode = await collection?.getVariableModeByName(modeName) - console.log(newVariableMode) - }, - - // Remove Variable Mode - removeVariableMode: async ( - variableCollection: VariableCollection, - variableMode: VariableMode, - ) => { - // Remove Variable Mode - variableMode?.remove() + getAllVariableModes: { + displayName: 'Get all variable modes', + code: async (variableCollection: VariableCollection) => { + // Get All Variable Modes + const variableModes = await variableCollection?.getAllVariableModes() + console.log(variableModes) + }, + }, + + getVariableModeById: { + displayName: 'Get variable mode by ID', + code: async ( + variableCollection: VariableCollection, + modeId: string, + ) => { + // Get Collection + const collection = variableCollection + + // Get Variable Mode by ID + const variableMode = await collection?.getVariableModeById(modeId) + console.log(variableMode) + }, + }, + + getVariableModeByName: { + displayName: 'Get variable mode by name', + code: async ( + variableCollection: VariableCollection, + modeName: string, + ) => { + // Get Collection + const collection = variableCollection + + // Get Variable Mode by Name + const variableMode = await collection?.getVariableModeByName(modeName) + console.log(variableMode) + }, + }, + + createVariableMode: { + displayName: 'Create variable mode', + code: async ( + variableCollection: VariableCollection, + modeName: string, + ) => { + // Get Collection + const collection = variableCollection + + // Create Variable Mode + const variableMode = await collection?.createVariableMode(modeName) + const newVariableMode = await collection?.getVariableModeByName(modeName) + console.log(newVariableMode) + }, + }, + + removeVariableMode: { + displayName: 'Remove variable mode', + code: async ( + variableCollection: VariableCollection, + variableMode: VariableMode, + ) => { + // Remove Variable Mode + variableMode?.remove() + }, }, }, } diff --git a/src/hooks/useFunctionCode.d.ts b/src/hooks/useFunctionCode.d.ts deleted file mode 100644 index b463b9f..0000000 --- a/src/hooks/useFunctionCode.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -export interface UseFunctionCodeResult { - functionCode: string - parameterNames: string[] - parameterTypes: string[] - functionParameters: Record - setParameterNames: (names: string[]) => void - setFunctionParameters: (params: Record) => void -} - -export function useFunctionCode( - selectedFunctionName: string, - selectedExampleCategory: string, -): UseFunctionCodeResult diff --git a/src/hooks/useFunctionCode.js b/src/hooks/useFunctionCode.js deleted file mode 100644 index fe4b6cf..0000000 --- a/src/hooks/useFunctionCode.js +++ /dev/null @@ -1,285 +0,0 @@ -import { useState, useEffect } from 'react' -import examples from '../examples/examples' - -// Import raw TypeScript files using Vite's ?raw suffix -import assetsRaw from '../examples/assets.ts?raw' -import componentsRaw from '../examples/components.ts?raw' -import elementsRaw from '../examples/elements.ts?raw' -import foldersRaw from '../examples/folders.ts?raw' -import pagesRaw from '../examples/pages.ts?raw' -import paymentsRaw from '../examples/payments.ts?raw' -import stylesRaw from '../examples/styles.ts?raw' -import variablesRaw from '../examples/variables.ts?raw' -import utilitiesRaw from '../examples/utilities.ts?raw' - -// Map category names to their raw file contents -const RAW_FILES_MAP = { - assets: assetsRaw, - components: componentsRaw, - elements: elementsRaw, - folders: foldersRaw, - pages: pagesRaw, - payments: paymentsRaw, - styles: stylesRaw, - variables: variablesRaw, - utilities: utilitiesRaw, -} - -// This hook is responsible for fetching and parsing function code, and extracting parameters. -export const useFunctionCode = ( - selectedFunctionName, - selectedExampleCategory, -) => { - const [functionCode, setFunctionCode] = useState('') - const [parameterNames, setParameterNames] = useState([]) - const [parameterTypes, setParameterTypes] = useState([]) - const [functionParameters, setFunctionParameters] = useState({}) - - useEffect(() => { - if (selectedFunctionName && selectedExampleCategory) { - // Get the raw file content from the imported files - const rawFileContent = - RAW_FILES_MAP[selectedExampleCategory.toLowerCase()] - - if (rawFileContent) { - try { - // Function to parse the function text and extract details - const functionMatch = parseFunctionText( - rawFileContent, - selectedFunctionName, - selectedExampleCategory, - ) - - if (functionMatch) { - let extractedCode = functionMatch - .replace(`${selectedFunctionName}:`, '') - .replace(/,\s*$/, '') - - // Strip the function wrapper to show only the body content - extractedCode = stripFunctionWrapper(extractedCode) - setFunctionCode(extractedCode) - - const { params, types } = extractParameters(functionMatch) - setParameterNames(params) - setParameterTypes(types) - - // Initialize function parameters with empty values - setFunctionParameters( - params.reduce((acc, param) => ({ ...acc, [param]: '' }), {}), - ) - } else { - setFunctionCode('Function code not found.') - setParameterNames([]) - setParameterTypes([]) - setFunctionParameters({}) - } - } catch (error) { - console.error('Failed to parse function source:', error) - setFunctionCode('Error parsing function code.') - setParameterNames([]) - setParameterTypes([]) - setFunctionParameters({}) - } - } else { - console.error( - 'Raw file not found for category:', - selectedExampleCategory, - ) - setFunctionCode('Example category not found.') - setParameterNames([]) - setParameterTypes([]) - setFunctionParameters({}) - } - } - }, [selectedFunctionName, selectedExampleCategory]) - - return { - functionCode, - parameterNames, - parameterTypes, - functionParameters, - setParameterNames, - setFunctionParameters, - } -} - -// Utility function to parse function text -const parseFunctionText = ( - text, - selectedFunctionName, - selectedExampleCategory, -) => { - // First, find the top-level category (Elements, Assets, etc.) - const findMatchingBrace = (str, startIndex) => { - let braceCount = 1 - let i = startIndex - - while (i < str.length && braceCount > 0) { - if (str[i] === '{') braceCount++ - if (str[i] === '}') braceCount-- - i++ - } - - return i - } - - // Find the start of the category - const categoryStart = text.indexOf( - `export const ${selectedExampleCategory} = {`, - ) - if (categoryStart === -1) { - console.error('Top level category not found:', selectedExampleCategory) - throw new Error(`Category "${selectedExampleCategory}" not found.`) - } - - // Find the matching closing brace - const contentStart = - categoryStart + `export const ${selectedExampleCategory} = {`.length - const contentEnd = findMatchingBrace(text, contentStart) - - // Extract everything between the braces - let searchText = text.slice(contentStart, contentEnd) - - // Handle nested function names (e.g., "elementManagement.setSelectedElement") - const [category, funcName] = selectedFunctionName.includes('.') - ? selectedFunctionName.split('.') - : [null, selectedFunctionName] - - // If we have a subcategory, extract that section - if (category) { - // Find the start of the subcategory - const subcategoryStart = searchText.indexOf(`${category}:`) - if (subcategoryStart === -1) { - console.error('Subcategory not found:', category) - throw new Error(`Subcategory "${category}" not found.`) - } - - // Find the opening brace - const braceStart = searchText.indexOf('{', subcategoryStart) - if (braceStart === -1) { - throw new Error(`No opening brace found for subcategory "${category}"`) - } - - // Find the matching closing brace using our existing helper - const subcategoryEnd = findMatchingBrace(searchText, braceStart + 1) - - // Extract everything between the braces - searchText = searchText.slice(braceStart + 1, subcategoryEnd - 1) - } - - // Regex to find all function definitions - const funcRegex = /(\w+):\s*(async\s*)?\(\s*.*?\)\s*=>\s*{(.*?)}/gs - - // Match all function definitions in the search text - const matches = [...searchText.matchAll(funcRegex)] - - // Find the match for the selected function - const selectedFunctionMatch = matches.find( - (match) => match[1] === (funcName || selectedFunctionName), - ) - - // If the function is not found, throw an error - if (!selectedFunctionMatch) { - throw new Error(`Function "${funcName || selectedFunctionName}" not found.`) - } - - // Get the index of the selected function in the matches - const selectedFunctionIndex = matches.findIndex( - (match) => match[1] === (funcName || selectedFunctionName), - ) - - // Get text for the selected function and everything up to the next function - const functionText = selectedFunctionMatch[0].trim() - - // If there's a next function, extract text up to it; otherwise, use the end of the text - const nextFunctionText = - selectedFunctionIndex + 1 < matches.length - ? matches[selectedFunctionIndex + 1][0] - : null - - // Determine the end index for extraction - const endIndex = nextFunctionText - ? searchText.indexOf(nextFunctionText) - : searchText.length - - // Extract everything from the function text up to the end index - let extractedText = searchText - .slice(searchText.indexOf(functionText), endIndex) - .trim() - - // Remove the trailing "}" if this is the last function in the file - if (!nextFunctionText) { - extractedText = extractedText.replace(/\s*}$/, '') - } - return extractedText -} - -// Utility function to strip the async function wrapper and show only the body -const stripFunctionWrapper = (code) => { - // Remove leading whitespace - let cleanedCode = code.trim() - - // Remove the async function wrapper pattern: async (...params) => { - // This regex matches: optional 'async', optional whitespace, optional params in parentheses, '=>', '{' - const functionWrapperRegex = /^\s*async\s*\([^)]*\)\s*=>\s*\{/ - if (functionWrapperRegex.test(cleanedCode)) { - // Remove the function declaration part - cleanedCode = cleanedCode.replace(functionWrapperRegex, '') - - // Remove the trailing closing brace and any trailing comma/whitespace - cleanedCode = cleanedCode.replace(/\s*\}\s*,?\s*$/, '') - } - - // Clean up any extra leading/trailing whitespace - cleanedCode = cleanedCode.trim() - - return cleanedCode -} - -// Utility function to extract parameters and types -const extractParameters = (functionMatch) => { - const paramsRegex = /\(\s*([^)]*?)\s*\)\s*=>/ - const paramsMatch = paramsRegex.exec(functionMatch) - const params = [] - const types = [] - if (paramsMatch) { - const paramString = paramsMatch[1] - const individualParamRegex = /(\w+)\s*:\s*(\w+)/g - let paramMatch - while ((paramMatch = individualParamRegex.exec(paramString))) { - params.push(paramMatch[1]) - types.push(paramMatch[2]) - } - } - return { params, types } -} - -const handleFunctionExecutionWithoutParameters = () => { - if (selectedExampleCategory && selectedFunctionName) { - // Handle nested function names (e.g., "elementManagement.setSelectedElement") - const [category, funcName] = selectedFunctionName.includes('.') - ? selectedFunctionName.split('.') - : [null, selectedFunctionName] - - // Get the top-level category - const topCategory = examples[selectedExampleCategory] - - // Get the function to execute, handling nested categories - const funcToExecute = category - ? topCategory[category][funcName] // For nested functions like elementManagement.getSelectedElement - : topCategory[selectedFunctionName] // For top-level functions - - if (funcToExecute && parameterNames.length === 0) { - try { - const result = funcToExecute() - if (result && typeof result.then === 'function') { - result.catch(console.error) - } else { - console.log(result) - } - } catch (error) { - console.error('Error executing function:', error) - } - } - } -} diff --git a/src/hooks/useFunctionCode.ts b/src/hooks/useFunctionCode.ts index cb89ff4..324d93e 100644 --- a/src/hooks/useFunctionCode.ts +++ b/src/hooks/useFunctionCode.ts @@ -175,8 +175,28 @@ const parseFunctionText = ( if (functionStart === -1) return null const functionContentStart = functionStart + `${functionName}: `.length - const functionContentEnd = findMatchingBrace(searchText, functionContentStart) + // Detect { displayName, code } object structure + const afterColon = searchText.slice(functionContentStart).replace(/^\s+/, '') + if (afterColon.startsWith('{')) { + // Find 'code: ' within this method's object + const codeMarker = 'code: ' + const codePropertyIndex = searchText.indexOf(codeMarker, functionContentStart) + if (codePropertyIndex !== -1) { + const codeContentStart = codePropertyIndex + codeMarker.length + // Find the opening { of the code function + const fnOpenBrace = searchText.indexOf('{', codeContentStart) + if (fnOpenBrace !== -1) { + const fnCloseEnd = findMatchingBrace(searchText, fnOpenBrace + 1) + // Include the function signature (async (...) => ) plus the body + const fnSignature = searchText.slice(codeContentStart, fnOpenBrace) + const fnBody = searchText.slice(fnOpenBrace, fnCloseEnd) + return `${functionName}: ${fnSignature}${fnBody}` + } + } + } + + const functionContentEnd = findMatchingBrace(searchText, functionContentStart) return searchText.slice(functionStart, functionContentEnd) } @@ -186,6 +206,20 @@ const stripFunctionWrapper = (code: string): string => { code = code.replace(/^[^{]*{/, '') code = code.replace(/}[^}]*$/, '') + // Dedent: find the minimum indentation of non-empty lines and strip it uniformly + const lines = code.split('\n') + const nonEmptyLines = lines.filter((line) => line.trim().length > 0) + if (nonEmptyLines.length > 0) { + const minIndent = Math.min( + ...nonEmptyLines.map((line) => line.match(/^(\s*)/)?.[1].length ?? 0), + ) + if (minIndent > 0) { + code = lines + .map((line) => (line.length >= minIndent ? line.slice(minIndent) : line)) + .join('\n') + } + } + // Remove leading/trailing whitespace code = code.trim() diff --git a/src/hooks/useMethodAnalysis.js b/src/hooks/useMethodAnalysis.js deleted file mode 100644 index 0b017fc..0000000 --- a/src/hooks/useMethodAnalysis.js +++ /dev/null @@ -1,79 +0,0 @@ -import { useEffect, useState } from 'react' -import * as acorn from 'acorn' -import * as walk from 'acorn-walk' -import acornTs from 'acorn-typescript' - -import { permissionsMap } from '../components/PermissionsMap' - -// This hook is responsible for extracting method calls and variable types from the function code. -export const useMethodAnalysis = (functionCode) => { - const [methodCalls, setMethodCalls] = useState([]) - const [variableTypes, setVariableTypes] = useState({}) - - useEffect(() => { - if (functionCode) { - const { methods, variables } = - extractMethodCallsAndVariableTypes(functionCode) - setMethodCalls(methods) - setVariableTypes(variables) - } - }, [functionCode]) - - return { methodCalls, variableTypes } -} - -// Utility function for extracting method calls and variable types -const extractMethodCallsAndVariableTypes = (code) => { - const cleanedCode = code.replace(/^\s*\w+:\s*/, '').replace(/},\s*$/, '}') - const ast = acorn.Parser.extend(acornTs()).parse(cleanedCode, { - ecmaVersion: 2020, - allowAwaitOutsideFunction: true, - sourceType: 'module', - }) - - const methodCalls = [] - const variableTypes = {} - - walk.simple(ast, { - CallExpression(node) { - if ( - node.callee?.type === 'MemberExpression' && - node.callee.object.name !== 'console' - ) { - const objectName = node.callee.object.name - const methodName = node.callee.property.name - const objectType = variableTypes[objectName] - methodCalls.push({ objectName, methodName, objectType }) - } - }, - VariableDeclarator(node) { - if (node.id && node.init && node.init.type === 'AwaitExpression') { - const variableName = node.id.name - const methodName = node.init.argument?.callee?.property?.name - if (methodName) { - Object.keys(permissionsMap).forEach((objectType) => { - if (permissionsMap[objectType]?.[methodName]) { - variableTypes[variableName] = objectType - } - }) - } - } - }, - AssignmentExpression(node) { - if ( - node.right.type === 'AwaitExpression' && - node.right.argument.type === 'CallExpression' - ) { - const methodName = node.right.argument.callee.property.name - const variableName = node.left.name - Object.keys(permissionsMap).forEach((objectType) => { - if (permissionsMap[objectType]?.[methodName]) { - variableTypes[variableName] = objectType - } - }) - } - }, - }) - - return { methods: methodCalls, variables: variableTypes } -}