import * as React from 'react' import cx from 'classnames' import { createHigherOrderComponent, } from '@wordpress/compose' import { hasBlockSupport, // @ts-expect-error } from '@wordpress/blocks' import { useMemo, } from '@wordpress/element' import { addFilter, } from '@wordpress/hooks' import { useStyleOverride, } from '@ska/utils' import { transformEditorStyles, } from '../../tailwind/css' import { BlockPreset, useStaticPresetsClassNames, } from '../presets' import type { tBlockAttributes, tBlockListBlockProps, tBlockNameOrType, tBlockType, } from '@ska/shared' import { useBlockInfo, useRecompile, useUpdateBlockInfo, } from './hooks' export interface TailwindValue { /** Tailwind class names. */ cx?: string /** Generated CSS. */ css?: string /** Last modified time. */ t?: number /** Presets */ p?: BlockPreset[] /** Sandbox/namespace block CSS (default: true). */ sb?: boolean } /** `skaBlocks` in block attributes. */ export interface TailwindAttributes { skaBlocks?: TailwindValue } export const hasTailwindSupport = (block: tBlockNameOrType) => hasBlockSupport(block, 'customClassName', true) const withCustomAttributes = (settings: tBlockType) => { if(!hasTailwindSupport(settings)) { return settings } if(!settings.attributes.skaBlocks) { Object.assign(settings.attributes, { skaBlocks: { type: 'object', }, }) } return settings } interface TailwindBlockListBlockProps extends tBlockListBlockProps { BlockListBlock: any isPreviewMode: boolean isInTemplate: boolean } /** Skips rendering TailwindBlockListBlock when the BlockListBlock is a clone, e.g. a repeated block in a Query Loop -> Post Template block. */ const MaybeTailwindBlockListBlock: React.FC> = props => { const { clientId, isSelectionEnabled, BlockListBlock, } = props const block = useBlockInfo(clientId, isSelectionEnabled) if(block.isClone) { return ( ) } return ( ) } /** Manages Tailwind classes on a block and loads their Tailwind CSS. */ const TailwindBlockListBlock: React.FC = ({ BlockListBlock, isPreviewMode, isInTemplate, // When it's in a template `useUpdateBlockInfo()` should be used to keep track of its' classes. ...props }) => { const { clientId, attributes, setAttributes, className: blockListBlockClassNames = '', isSelectionEnabled, } = props const { skaBlocks = {}, } = attributes const { cx: tailwindClassNames = '', css = '', sb = true, } = skaBlocks const staticPresetsClassNames = useStaticPresetsClassNames(attributes) const className = cx(staticPresetsClassNames, tailwindClassNames, blockListBlockClassNames) const isRecompileCandidate = ( !isPreviewMode && isSelectionEnabled && 'skaBlocks' in attributes && attributes.skaBlocks !== undefined ) useRecompile(clientId, isRecompileCandidate, attributes, setAttributes) /** Adds `#${clientId}` prefix to selectors. */ const transformedStyles = useMemo(() => transformEditorStyles(css, clientId, className, sb), [clientId, css, className, sb]) useStyleOverride(`ska-blocks-tw--${clientId}`, transformedStyles) useUpdateBlockInfo(clientId, isInTemplate, className) return } const withTailwind = createHigherOrderComponent( (BlockListBlock: any) => (props: tBlockListBlockProps) => { if(hasTailwindSupport(props.name)) { return ( } BlockListBlock={BlockListBlock} /> ) } return }, 'withSkaBlocksTailwind' ) /** Add Tailwind class names to saved block. */ const withTailwindClassNames = (props: {className?: string}, blockType: tBlockType, attributes: tBlockAttributes): object => { const { skaBlocks = {}, } = attributes const { cx: tailwindClassNames = '', } = skaBlocks if(!tailwindClassNames) { return props } return { ...props, className: cx(tailwindClassNames, props.className), } } export default () => { addFilter('blocks.registerBlockType', 'ska-blocks/with-tailwind-attributes', withCustomAttributes) addFilter('editor.BlockListBlock', 'ska-blocks/with-tailwind', withTailwind, 500) addFilter('blocks.getSaveContent.extraProps', `ska-blocks/with-tailwind-classes`, withTailwindClassNames) }