import { titleize, underscore } from 'inflection' import type * as Style from '../index.js' import * as Definitions from './definitions.js' export function getLabelByName(name: string) { return name == 'container' ? 'Block' : name == 'media' ? 'Image' : titleize(underscore(name)) } /** * Generate selectors that match without styling qualifier. Given class like `-card` it should be able match eleenets * with classes `-card`, `-card--test`, `test -card`, `test -card--test`, but not `test-card` * * @example * * relaxClassName('-card') // ['[class^="-card"]', '[class*=" -card"]'] * relaxClassName('-block--inline') // ['[class^="-block--inline"]', '[class*=" -block--inline"]'] * */ export function relaxClassName(className: string) { return [`[class^="${className}"]`, `[class*=" ${className}"]`] } /** * Get selectors used to match the definition. If definition disguises as another, it recurses to resolved target * definition. */ export function getDefinitionSelectors(definition: Style.ElementDefinition): string[] { return (definition.disguise ? ['.' + definition.className] : relaxClassName(definition.className)) .concat(definition.htmlTagName) .concat( Definitions.all .filter((d) => d.disguise === definition.name) .map(getDefinitionSelectors) .flat() ) .flat(Infinity) .filter(Boolean) } /** Class is `-button`/`-button--style`, or `-block--badge`/`-block--badge--style` */ export function isElementClassName(className: string) { if (className.startsWith('-block--') || className.startsWith('-inline--') || className.startsWith('-component--')) return true return !!getImplicitElementName(className) } /** * Return element name implied by a class name. * * @example * * getImplicitElementName('-section--test') // section * getImplicitElementName('-button') // button * */ export function getImplicitElementName(className: string) { return getDefinitionFromClassName(className)?.name ?? null } /** * Find element definition matching class name. * * @example * * getDefinitionFromClassName('-section--cool') // Style.definition..section * getDefinitionFromClassName('-block--test--style') // Style.definition..block * */ export function getDefinitionFromClassName(className: string) { if (!className) return return Definitions.all.find((d) => matchClassName(className, d.className)) } export function matchClassName(className: string, candidate: string) { return ( className.startsWith(candidate) && (className.length == candidate.length || className.substring(candidate.length, candidate.length + 2) == '--') ) } /** * Remove style qualifier from a qualifier, this will effectively revert element back to theme style. * * @example * * getElementGenericClassName('-button') // '-button' * getElementGenericClassName('-section--test') // '-section' * getElementGenericClassName('-inline--chip--big') // '-inline--chip' * */ export function getElementGenericClassName(className: string) { if (className == null || !isElementClassName(className)) return null const bits = className.split('--') if (bits[0] == '-inline' || bits[0] == '-block' || bits[0] == '-component') { return bits.slice(0, 2).join('--') } else { return bits[0] } } /** * Returns element style type by class name (text/block/inline) * * @example * * getElementType('-section--test') // 'block' * getElementType('-heading1') // 'text' * getElementType('-inline--badge--small') // 'inline' * */ export function getElementType(className: string): Style.Type.Element { if (className == null) return null const defaultClassName = getElementGenericClassName(className) const customCategory = defaultClassName.match(/\-(block|inline|text)/)?.[1] as Style.Type.Element if (customCategory) return customCategory const tagName = getImplicitElementName(defaultClassName) return Definitions.all.find((d) => d.name == tagName)?.type }