now-design-atoms
Version:
Atomic UI components for now-design.
183 lines (176 loc) • 9.12 kB
JavaScript
;
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;