import { useState, useEffect, useCallback, memo } from '@wordpress/element'; import { Button, SelectControl, Disabled } from '@wordpress/components'; import apiFetch from '@wordpress/api-fetch'; import type { ResponseProps } from '@components/DesignPanel/types'; import { Notice, Spinner, TextControl, ToggleControl, } from '@wordpress/components'; import { TabsWrapper, TabsContent, TabsList } from '@components/ui/Tabs'; import has from 'lodash/has'; import { Modal, ModalClose, ModalContent, ModalFooter, ModalHeader, ModalTitle, ModalTrigger, } from '@components/ui/Modal'; import { DisappearingMessage } from '@components/ui/DisappearingMessage'; import { useSelect } from '@wordpress/data'; import PageIcon from 'blockbite-icons/dist/Pencil1'; import { Icon as IconComp } from '@components/ui/Icon'; export type DesignTokenProp = { token: string; value: string; name: string; optional?: any; }; export type DesignTokenProps = { fontSizes: any[]; colors: any[]; fonts: any[]; headings: any[]; }; export type DesignTokenOptinProps = { colors: boolean; fonts: boolean; fontSizes: boolean; headings: boolean; }; const FONTSIZESLOTS = 16; const COLORSSLOTS = 16; const FONTSSLOTS = 10; const HEADINGSLOTS = 7; export const createSlots = (themeValues: any) => { const { colors: colorValues = [], fonts: fontsValues = [], fontSizes: fontSizesValues = [], headings: headingsValues = [], } = themeValues; const createSlotsArray = ( count: number, tokenPrefix: string, names: string[] ) => { return Array.from({ length: count }, (_, i) => ({ token: `${tokenPrefix}-${i + 1}`, value: '', name: names.length ? names[i] : `${tokenPrefix}-${i + 1}`, optional: {}, })); }; // Generate slots for each category with specified lengths const fontSizes = createSlotsArray(FONTSIZESLOTS, 'b_fontsize', []); const colors = createSlotsArray(COLORSSLOTS, 'b_color', []); const fonts = createSlotsArray(FONTSSLOTS, 'b_font', []); const headings = createSlotsArray(HEADINGSLOTS, 'b_heading', [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', ]); const populateSlots = (slots: any[], data: any[]) => { (data || []).forEach((item, index) => { if (slots[index]) { slots[index].value = item.value || ''; slots[index].name = item.name || slots[index].name; if (item.optional) { slots[index].optional = { ...item.optional }; } } }); }; // Populate the slots with provided theme values populateSlots(fontSizes, fontSizesValues); populateSlots(colors, colorValues); populateSlots(fonts, fontsValues); populateSlots(headings, headingsValues); return { fontSizes, colors, fonts, headings }; }; export const saveDesignTokens = async ( themeOptin: DesignTokenOptinProps, inputFields: DesignTokenProps ) => { /* get native global styles global styles are written as a custom post type in the database if you change the site editor styles */ const nativeGlobalStyles = await apiFetch({ path: '/blockbite/v1/editor-settings/native-global-styles', }).then((res: any) => { return res; }); // get slots directly from native theme const updatedTheme: DesignTokenProps = { ...blockbite.getNativeTheme(nativeGlobalStyles), }; // check any optin is enabled and override them with enabled fields if (themeOptin.colors) { updatedTheme.colors = inputFields.colors; } if (themeOptin.fonts) { updatedTheme.fonts = inputFields.fonts; } if (themeOptin.fontSizes) { updatedTheme.fontSizes = inputFields.fontSizes; } if (themeOptin.headings) { updatedTheme.headings = inputFields.headings; } // fill remaining empty slots const slotTheme = createSlots(updatedTheme); // save current optin state apiFetch({ path: `/blockbite/v1/items/upsert-handle`, method: 'POST', data: { handle: 'design-tokens-optin', content: JSON.stringify(themeOptin), }, }); // save design tokens return apiFetch({ path: `/blockbite/v1/items/upsert-handle`, method: 'POST', data: { handle: 'design-tokens', platform: 'site', content: JSON.stringify(slotTheme), }, }).then((res: ResponseProps) => { if (res.status === 200) { // this will trigger to update the tailwind config wp.data.dispatch('biteStore/editor').setTheme(slotTheme); return res; } }); }; export const changeThemeOptin = (theme: DesignTokenOptinProps) => { apiFetch({ path: `/blockbite/v1/items/upsert-handle`, method: 'POST', data: { handle: 'design-tokens-optin', platform: 'site', content: JSON.stringify(theme), }, }); }; const FieldMapper = memo( ({ fieldType, inputFields, setInputFields, themeOptin, disableLabels = false, setMessage, }: any) => { const handleInputChange = useCallback( (token: any, key: any, value: any) => { setInputFields((prevFields: any) => ({ ...prevFields, [fieldType]: prevFields[fieldType].map((item: any) => item.token === token ? { ...item, [key]: value } : item ), })); }, [fieldType, setInputFields] ); return inputFields[fieldType].map((item: any) => (
Sync your design tokens from the{' '} Site Editor {' '} or theme.json to blockbite. Syncing will not affect custom design tokens.