import { select, } from '@wordpress/data' import { store as coreDataStore, } from '@wordpress/core-data' import { parseJson, } from '@ska/utils' import { mergeAttributes, mergeAttributeValues, type TailwindFeatureAttribute, } from '../../tailwind/attributes' import type { tBlockAttributes, } from '@ska/shared' import type { BlockPreset, Preset, PresetBlockAttributes, Presets, PresetSlug, } from '.' import { type TailwindValue, } from '../tailwind' import { managedPresets, } from '../../data' /** * Managed preset cannot be deleted, but can be reset to default. */ export const isManagedPreset = (slug: PresetSlug) => { return Object(managedPresets).hasOwnProperty(slug) } export const resolvePresetSlug = (slug: BlockPreset['id'], presets: Presets): PresetSlug | undefined => { if(Object(presets).hasOwnProperty(slug)) { return slug as PresetSlug } /** Back-compat. */ if(Number.isInteger(slug)) { const found = Object.entries(presets).map(([slug, preset]) => ({slug, ...preset})).find(({__id}) => { return __id === slug }) if(found) { return found.slug } } return undefined } export const resolvePreset = (slug: BlockPreset['id'], presets: Presets): Preset => { const resolvedSlug = resolvePresetSlug(slug, presets) if(resolvedSlug && Object(presets).hasOwnProperty(resolvedSlug)) { return presets[resolvedSlug] } return { ref: '404', title: `Not found (${slug.toString()})`, attrs: '{}', cx: '', css: '', loading: undefined, } } export const mergePresets = (presets: PresetBlockAttributes[]) => { return presets.reduce((acc, presetAttributes) => { return Object.keys(presetAttributes).reduce((attributes, key) => { if(attributes[key]) { attributes[key] = mergeAttributeValues(attributes[key], presetAttributes[key]) } else { attributes[key] = presetAttributes[key] } return attributes }, acc) }, {} as Record) } /** Merges and returns only values that exist in the preset. Used when merging an existing preset into block's attributes. */ export const mergePresetAndAttributes = (preset: tBlockAttributes, attributes: tBlockAttributes, base: tBlockAttributes = {}): tBlockAttributes => { /** Keys of `preset` are `skaBlocks*` values stored in preset, while keys of `attributes` are all block attributes, such as `content` etc. */ return Object.keys(preset).reduce((acc, key) => { switch(key) { case 'skaBlocksSelectors': const presetSelectors = preset[key] || {} const targetSelectors = attributes[key] || {} acc[key] = Object.keys(presetSelectors).reduce((a, c) => { return { ...a, [c]: mergePresetAndAttributes(presetSelectors[c] || {}, {}, targetSelectors[c] || {}), } }, targetSelectors) break case 'skaBlocksVariables': const presetVariables = preset[key] || {} const targetVariables = attributes[key] || {} const baseVariables = base[key] || {} const {record: presetVariablesRecord = {}} = presetVariables const {record: targetVariablesRecord = {}} = targetVariables const {record: baseVariablesRecord = {}} = baseVariables acc[key] = { ...baseVariables, ...targetVariables, ...presetVariables, record: { ...baseVariablesRecord, ...targetVariablesRecord, ...presetVariablesRecord, }, } break default: acc[key] = mergeAttributeValues(preset[key], attributes[key]) } return acc }, base as tBlockAttributes) // Base is only provided when merging nested selectors. } export const presetsWithAttributes = (presets: PresetBlockAttributes[], attributes: tBlockAttributes): tBlockAttributes => { if(!presets.length) { return attributes } return mergeAttributes(mergePresets(presets), attributes) } export type PresetFilters = 'static' | 'dynamic' export const getPresetSlugs = (attributes: tBlockAttributes, filter?: PresetFilters) => { const { skaBlocks = {}, } = attributes const { p = [], } = skaBlocks as TailwindValue return p.map(({id, isStatic = false}) => { if(filter === 'static' && !isStatic) { return false } if(filter === 'dynamic' && isStatic) { return false } return id }).filter(Boolean) as PresetSlug[] } export const getPresetsAttributes = (slugs: PresetSlug[] = [], presets: Presets) => { return slugs.map(slug => { const { attrs = '{}', } = presets[slug] || {} return parseJson(attrs, `Preset: ${slug}`) as PresetBlockAttributes }) } /** * Combine `t`-s of expired dynamic presets into a string that is used to keep track of recompilation. * When the string is empty there are no expired presets. */ export const getExpiredDynamicPresetsKey = (blockPresets: BlockPreset[] = [], presets: Presets = {}) => { if(!blockPresets.length) { return '' } const dynamicBlockPresets = blockPresets.filter(({isStatic = false}) => { return !isStatic }) if(!dynamicBlockPresets.length) { return '' } return dynamicBlockPresets.filter(({id, t}) => { if(!id || !Object(presets).hasOwnProperty(id)) { return false } const {t: presetT = -1} = presets[id] return presetT > 0 && presetT !== t }).map(({t}) => t).join('-') } export const getPresetsOption = () => { const {getEditedEntityRecord} = select(coreDataStore) const site = getEditedEntityRecord('root', 'site', undefined!) const {ska_blocks_presets = {}} = (site || {}) as any const {presets = {}} = ska_blocks_presets return presets as Presets }