UNPKG

now-design-atoms

Version:

Atomic UI components for now-design.

183 lines (176 loc) 9.12 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _react = _interopRequireDefault(require("react")); var _jsxRuntime = require("react/jsx-runtime"); var _excluded = ["icon", "size", "color", "ariaLabel", "title", "className", "style", "onClick", "tabIndex", "role", "spin", "disabled", "direction", "strokeWidth", "fill", "focusable"]; function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; } function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; } function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } // IMPORTANT: Ensure this import is present at the app entry point for tokens to work // import 'now-design-tokens/dist/css/variables.css'; var directionMap = { up: 'rotate(0deg)', right: 'rotate(90deg)', down: 'rotate(180deg)', left: 'rotate(270deg)' }; // Helper function to determine if a value should be treated as a token or direct value var getTokenValue = function getTokenValue(value) { var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'size'; if (typeof value === 'number') { return type === 'size' ? "".concat(value, "px") : value; } if (typeof value === 'string') { // Check if it's a CSS color value (hex, rgb, hsl, etc.) if (type === 'color' && (value.startsWith('#') || value.startsWith('rgb') || value.startsWith('hsl'))) { return value; } // Check if it's a pixel value if (type === 'size' && value.endsWith('px')) { return value; } // Otherwise treat as token name return "var(--".concat(value, ")"); } return value; }; // Helper function to validate and resolve icon component var validateIcon = function validateIcon(icon) { if (!icon) { return null; } if (typeof icon === 'string') { return null; } if (typeof icon !== 'function' && _typeof(icon) !== 'object') { return null; } // Check if it's a valid React component if (typeof icon === 'function') { // It's a function component or class component return icon; } if (_typeof(icon) === 'object') { // Check if it has a render method (class component) or $$typeof (React element) if (icon.render || icon.$$typeof) { return icon; } return null; } return icon; }; /** * Generic icon atom that renders a provided SVG React component with token-aware sizing and color. * Includes conveniences for rotation, spinning animation, disabled state, and accessibility. * * @param {Object} props * @param {React.ComponentType|React.ReactElement} props.icon Required icon component or element to render. * @param {number|string} [props.size='icon-md'] Pixel number, 'NNpx', or token name for width/height. * @param {string} [props.color='icon-primary'] Token name or raw CSS color for `color`. * @param {string} [props.ariaLabel] Accessible label; if omitted, icon is marked aria-hidden. * @param {string} [props.title] Title attribute for native tooltips. * @param {string} [props.className] Additional wrapper classes. * @param {Object} [props.style={}] Inline style overrides for the wrapper. * @param {function(MouseEvent): void} [props.onClick] Click handler (disabled when `disabled`). * @param {number} [props.tabIndex] Tab index for keyboard navigation. * @param {'img'|'presentation'} [props.role='img'] Accessible role for the wrapper. * @param {boolean} [props.spin=false] Applies a CSS spinning animation. * @param {boolean} [props.disabled=false] Disables interactions and dims the icon. * @param {'up'|'right'|'down'|'left'} [props.direction] Rotates the icon to the given direction. * @param {number} [props.strokeWidth] Stroke width forwarded to the SVG. * @param {string} [props.fill] Fill color forwarded to the SVG. * @param {boolean} [props.focusable=false] Sets focusability attributes on wrapper and SVG. * @returns {JSX.Element} Span wrapper containing the rendered icon component. */ var IconAtom = function IconAtom(_ref) { var Icon = _ref.icon, _ref$size = _ref.size, size = _ref$size === void 0 ? 'icon-md' : _ref$size, _ref$color = _ref.color, color = _ref$color === void 0 ? 'icon-primary' : _ref$color, ariaLabel = _ref.ariaLabel, title = _ref.title, className = _ref.className, _ref$style = _ref.style, style = _ref$style === void 0 ? {} : _ref$style, onClick = _ref.onClick, tabIndex = _ref.tabIndex, _ref$role = _ref.role, role = _ref$role === void 0 ? 'img' : _ref$role, _ref$spin = _ref.spin, spin = _ref$spin === void 0 ? false : _ref$spin, _ref$disabled = _ref.disabled, disabled = _ref$disabled === void 0 ? false : _ref$disabled, direction = _ref.direction, strokeWidth = _ref.strokeWidth, fill = _ref.fill, _ref$focusable = _ref.focusable, focusable = _ref$focusable === void 0 ? false : _ref$focusable, rest = _objectWithoutProperties(_ref, _excluded); // Validate the icon component var IconComponent = validateIcon(Icon); // If no valid icon component found, render a helpful placeholder if (!IconComponent) { return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { role: role, "aria-label": ariaLabel || 'Missing icon', className: className, style: { border: '1px dashed #ccc', color: '#999', fontSize: '12px', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: getTokenValue(size, 'size'), height: getTokenValue(size, 'size') }, title: "Icon component missing - check console for details", children: "?" }); } // The Icon component should be dimensionless - it just passes props to the SVG var iconStyle = _objectSpread({ color: getTokenValue(color, 'color'), display: 'inline-flex', alignItems: 'center', justifyContent: 'center', userSelect: 'none', pointerEvents: disabled ? 'none' : undefined, opacity: disabled ? 0.5 : 1, transform: direction ? directionMap[direction] : undefined, animation: spin ? 'icon-spin 1s linear infinite' : undefined }, style); return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", _objectSpread(_objectSpread({ role: role, "aria-label": ariaLabel, "aria-disabled": disabled || undefined, tabIndex: tabIndex, className: className, style: iconStyle, onClick: disabled ? undefined : onClick, title: title, focusable: focusable ? 'true' : 'false' }, rest), {}, { children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(IconComponent, { width: getTokenValue(size, 'size'), height: getTokenValue(size, 'size'), strokeWidth: strokeWidth, fill: fill, "aria-hidden": ariaLabel ? undefined : 'true', focusable: focusable ? 'true' : 'false', "data-testid": rest['data-testid'] }), /*#__PURE__*/(0, _jsxRuntime.jsx)("style", { children: "\n @keyframes icon-spin {\n 100% { transform: rotate(360deg); }\n }\n " })] })); }; var _default = exports["default"] = IconAtom;