UNPKG

@meonode/ui

Version:

A structured approach to component composition with built-in theming, prop separation, and dynamic children handling.

82 lines 11.4 kB
function _createForOfIteratorHelper(b,c){var d="undefined"!=typeof Symbol&&b[Symbol.iterator]||b["@@iterator"];if(!d){if(Array.isArray(b)||(d=_unsupportedIterableToArray(b))||c&&b&&"number"==typeof b.length){d&&(b=d);var e=0,f=function F(){};return{s:f,n:function n(){return e>=b.length?{done:!0}:{done:!1,value:b[e++]}},e:function e(a){throw a},f:f}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var g,h=!0,i=!1;return{s:function s(){d=d.call(b)},n:function n(){var a=d.next();return h=a.done,a},e:function e(a){i=!0,g=a},f:function f(){try{h||null==d["return"]||d["return"]()}finally{if(i)throw g}}}}function _slicedToArray(a,b){return _arrayWithHoles(a)||_iterableToArrayLimit(a,b)||_unsupportedIterableToArray(a,b)||_nonIterableRest()}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _unsupportedIterableToArray(b,c){if(b){if("string"==typeof b)return _arrayLikeToArray(b,c);var a={}.toString.call(b).slice(8,-1);return"Object"===a&&b.constructor&&(a=b.constructor.name),"Map"===a||"Set"===a?Array.from(b):"Arguments"===a||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(a)?_arrayLikeToArray(b,c):void 0}}function _arrayLikeToArray(b,c){(null==c||c>b.length)&&(c=b.length);for(var d=0,f=Array(c);d<c;d++)f[d]=b[d];return f}function _iterableToArrayLimit(b,c){var d=null==b?null:"undefined"!=typeof Symbol&&b[Symbol.iterator]||b["@@iterator"];if(null!=d){var g,h,j,k,l=[],a=!0,m=!1;try{if(j=(d=d.call(b)).next,0===c){if(Object(d)!==d)return;a=!1}else for(;!(a=(g=j.call(d)).done)&&(l.push(g.value),l.length!==c);a=!0);}catch(a){m=!0,h=a}finally{try{if(!a&&null!=d["return"]&&(k=d["return"](),Object(k)!==k))return}finally{if(m)throw h}}return l}}function _arrayWithHoles(a){if(Array.isArray(a))return a}function _typeof(a){"@babel/helpers - typeof";return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a},_typeof(a)}import{useEffect,useMemo}from"react";// Manages styles for server-side rendering (SSR) var serverStyles=[],serverRuleIndex=0;/** * Retrieves the concatenated CSS string for server-side rendering. * @returns {string} The CSS string. */export function getServerStyles(){return serverStyles.join("\n")}// Manages styles for client-side rendering var styleSheet;if("undefined"!=typeof document){// Ensures a style tag with id '__sx' exists in the document head var styleTag=document.head.querySelector("#__sx")||function(){var a=document.createElement("style");return a.id="__sx",document.head.appendChild(a),a}();styleSheet=styleTag.sheet}// Global index for generating unique class names var ruleIndex=0,sxCache=new Map,refCountMap=new Map,ruleMap=new Map,unitlessProperties=new Set(["animationIterationCount","borderImageOutset","borderImageSlice","borderImageWidth","boxFlex","boxFlexGroup","boxOrdinalGroup","columnCount","columns","flex","flexGrow","flexPositive","flexShrink","flexNegative","flexOrder","gridRow","gridRowEnd","gridRowSpan","gridRowStart","gridColumn","gridColumnEnd","gridColumnSpan","gridColumnStart","fontWeight","lineClamp","lineHeight","opacity","order","orphans","tabSize","widows","zIndex","zoom","fillOpacity","floodOpacity","stopOpacity","strokeDasharray","strokeDashoffset","strokeMiterlimit","strokeOpacity","strokeWidth"]);// Cache for mapping style objects to generated class names // Reference count for each cached style, used for knowing when to remove styles // Maps cache keys to the array of CSS rules it generated // Type definitions for CSS values and style objects // Set of CSS properties that do not require a 'px' unit when their value is a number /** * Custom React hook to generate and manage CSS classes from a style object. * Supports nesting, pseudo-selectors, and at-rules (@media, @container, @supports). * Handles both client-side and server-side rendering. * @param {StyleObject} sxObject The style object defining the CSS. * @param {any[]} deps Dependencies for memoization, similar to `useEffect` or `useMemo`. * @returns {string} The generated CSS class name. */export function useClasses(a){var b=1<arguments.length&&void 0!==arguments[1]?arguments[1]:[],c=useMemo(function(){return"".concat(stableStringify(a),"::").concat(stableStringify(b))},[a,b]);// Generates a stable cache key based on the style object and dependencies // Manages the reference count for the generated style, cleaning up when no longer used. // Returns the cached class name if the style has already been generated if(useEffect(function(){// Decrement reference count and clean up styles when component unmounts return refCountMap.set(c,(refCountMap.get(c)||0)+1),function(){var a=refCountMap.get(c)||1;1===a?(removeStyleRules(c),sxCache["delete"](c),refCountMap["delete"](c)):refCountMap.set(c,a-1)}},[c]),sxCache.has(c))return sxCache.get(c);// Generates a new unique class name var d="sx-".concat(ruleIndex++),e=[],f={},g={},h={},i={};// Stores the generated CSS rules // Stores styles directly applicable to the base class or its nested selectors // Stores raw style objects for @media rules // Stores raw style objects for @container rules // Stores raw style objects for @supports rules // First pass: Separates top-level styles from at-rules for(var j in a){var k=a[j];j.startsWith("@media")?g[j]=k:j.startsWith("@container")?h[j]=k:j.startsWith("@supports")?i[j]=k:f[j]=k}// Collects all rules for the base class and its nested selectors // Injects styles based on the rendering environment if(e.push(...collectRules(f,".".concat(d))),processAtRuleBlocks(g,e,d),processAtRuleBlocks(h,e,d),processAtRuleBlocks(i,e,d),"undefined"==typeof document){// Server-side rendering: stores CSS and returns a server-specific class name var l="sx-".concat(serverRuleIndex++),m=e.map(function(a){return a.replace(new RegExp(d,"g"),l)}).join("\n");return serverStyles.push(m),sxCache.set(c,l),l}// Caches the generated rules and class name return styleSheet&&// Client-side injection: inserts rules into the stylesheet injectStyles(e),ruleMap.set(c,e),sxCache.set(c,d),d}/** * Recursively collects CSS rules from a style object. * Handles direct properties, nested selectors (prefixed with '&'), and warns about deeply nested at-rules. * @param {StyleObject} sxBlock The style object to process. * @param {string} currentSelector The current CSS selector context. * @returns {string[]} An array of complete CSS rules. */function collectRules(a,b){var c=[],d={};// Stores direct CSS properties for the current selector for(var e in a){var f=a[e];if("object"!==_typeof(f)||null===f)d[e]=f;else if(e.startsWith("&")){// Handles nested selectors (e.g., '&:hover', '& span') var g=e.replace(/&/g,b);c.push(...collectRules(f,g))}else e.startsWith("@")?// Warns about deeply nested at-rules, as they are processed at top-level or directly under the base class. console.warn("Nested at-rule found at key \"".concat(e,"\" within selector \"").concat(b,"\". Current implementation processes at-rules at the top level or directly under the base class.")):// Flattens plain nested objects into the current selector's declarations Object.assign(d,f)}// Generates the CSS rule for the current selector's direct properties var h=toCssDeclarations(d);return h&&c.unshift("".concat(b," { ").concat(h," }")),c}/** * Processes at-rule blocks (@media, @container, @supports) and adds their rules to the output. * @param {Record<string, StyleObject>} blocksMap A map where keys are at-rules (e.g., '@media (min-width: 768px)') and values are their style objects. * @param {string[]} output The array to which generated CSS rules are added. * @param {string} baseClassName The base class name to which the at-rule styles apply. */function processAtRuleBlocks(a,b,c){for(var d=0,e=Object.entries(a);d<e.length;d++){var f=_slicedToArray(e[d],2),g=f[0],h=f[1],i=collectRules(h,".".concat(c));// Collects rules for the at-rule's content, using the base class name for context i.length&&b.push("".concat(g," { ").concat(i.join(" ")," }"))}}/** * Injects an array of CSS rules into the document's stylesheet. * @param {string[]} rules An array of complete CSS rule strings. */function injectStyles(a){if(styleSheet){var b,c=_createForOfIteratorHelper(a);try{for(c.s();!(b=c.n()).done;){var d=b.value;try{styleSheet.insertRule(d,styleSheet.cssRules.length)}catch(a){console.error("Failed to insert CSS rule: ".concat(d),a)}}}catch(a){c.e(a)}finally{c.f()}}}/** * Removes CSS rules associated with a specific cache key from the stylesheet. * @param {string} cacheKey The cache key identifying the rules to remove. */function removeStyleRules(a){var b=ruleMap.get(a);if(b&&styleSheet){// Iterates in reverse to safely delete rules without affecting indices for(var c,d=styleSheet.cssRules.length-1;0<=d;d--)c=styleSheet.cssRules[d],b.includes(c.cssText)&&styleSheet.deleteRule(d);ruleMap["delete"](a)}}/** * Converts a flat style object into a CSS declaration string (e.g., "prop: value;"). * Automatically appends 'px' to number values for non-unitless properties. * @param {StyleObject} obj The flat style object containing CSS property-value pairs. * @returns {string} The CSS declaration string. */function toCssDeclarations(a){var b=[];for(var c in a){var d=a[c];// Warns if a nested object is encountered, as this function expects flat properties. if("object"===_typeof(d)&&null!==d){console.warn("toCssDeclarations received a nested object for key \"".concat(c,"\". This function only processes flat CSS property-value pairs."));continue}"number"!=typeof d||isUnitlessProperty(c)?b.push("".concat(camelToKebab(c),": ").concat(d,";")):b.push("".concat(camelToKebab(c),": ").concat(d,"px;"))}return b.join(" ")}/** * Checks if a given CSS property is unitless. * @param {string} prop The CSS property name (camelCase). * @returns {boolean} True if the property is unitless, false otherwise. */function isUnitlessProperty(a){return unitlessProperties.has(a)}/** * Generates a stable JSON string representation of an object. * Ensures consistent stringification regardless of property order, useful for cache keys. * @param {any} obj The object to stringify. * @returns {string} The stable JSON string. */function stableStringify(a){if(null===a||"object"!==_typeof(a))return JSON.stringify(a);if(Array.isArray(a))return"[".concat(a.map(stableStringify).join(","),"]");var b=Object.keys(a).sort(),c=b.map(function(b){return"\"".concat(b,"\":").concat(stableStringify(a[b]))});return"{".concat(c.join(","),"}")}/** * Converts a camelCase string to kebab-case. * @param {string} str The camelCase string. * @returns {string} The kebab-case string. */function camelToKebab(a){return a.replace(/([A-Z])([A-Z])/g,"$1-$2").replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}/** * Clears all generated styles and resets the internal state. * Useful for development or testing environments to ensure a clean slate. */export function purgeStyles(){if(styleSheet)for(;0<styleSheet.cssRules.length;)styleSheet.deleteRule(0);serverStyles=[],sxCache.clear(),ruleMap.clear(),refCountMap.clear(),ruleIndex=0,serverRuleIndex=0}