@redocly/theme 0.56.0-next.13 → 0.56.0-next.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/lib/components/Catalog/Catalog.d.ts +1 -1
  2. package/lib/components/Catalog/CatalogCardView/CatalogCard.js +2 -2
  3. package/lib/components/Catalog/CatalogSelector.d.ts +1 -1
  4. package/lib/components/Catalog/CatalogSelector.js +4 -3
  5. package/lib/components/Catalog/CatalogViewModeToggle.d.ts +1 -1
  6. package/lib/components/Dropdown/Dropdown.js +1 -0
  7. package/lib/components/Dropdown/variables.js +1 -0
  8. package/lib/components/Footer/FooterItem.js +5 -10
  9. package/lib/components/Footer/variables.js +2 -2
  10. package/lib/components/Markdown/Markdown.js +7 -7
  11. package/lib/components/Menu/MenuItem.js +5 -15
  12. package/lib/components/Navbar/NavbarItem.js +8 -48
  13. package/lib/core/types/sidebar.d.ts +1 -0
  14. package/lib/core/utils/icon-resolver.js +1 -4
  15. package/lib/ext/configure.d.ts +1 -1
  16. package/lib/icons/CDNIcon/CDNIcon.js +2 -2
  17. package/lib/icons/UniversalIcon/UniversalIcon.d.ts +11 -0
  18. package/lib/icons/UniversalIcon/UniversalIcon.js +61 -0
  19. package/lib/index.d.ts +1 -0
  20. package/lib/index.js +1 -0
  21. package/lib/layouts/CodeWalkthroughLayout.js +2 -2
  22. package/lib/markdoc/components/Cards/CardIcon.js +7 -19
  23. package/lib/markdoc/components/Tabs/Tab.d.ts +2 -1
  24. package/lib/markdoc/components/Tabs/Tab.js +5 -2
  25. package/lib/markdoc/components/Tabs/Tabs.d.ts +1 -1
  26. package/lib/markdoc/components/Tabs/variables.js +2 -0
  27. package/lib/markdoc/tags/card.js +1 -1
  28. package/lib/markdoc/tags/tab.js +1 -0
  29. package/package.json +1 -1
  30. package/src/components/Catalog/Catalog.tsx +1 -1
  31. package/src/components/Catalog/CatalogCardView/CatalogCard.tsx +1 -1
  32. package/src/components/Catalog/CatalogSelector.tsx +3 -1
  33. package/src/components/Catalog/CatalogViewModeToggle.tsx +1 -1
  34. package/src/components/Dropdown/Dropdown.tsx +1 -0
  35. package/src/components/Dropdown/variables.ts +1 -0
  36. package/src/components/Footer/FooterItem.tsx +10 -21
  37. package/src/components/Footer/variables.ts +2 -2
  38. package/src/components/Markdown/Markdown.tsx +1 -1
  39. package/src/components/Menu/MenuItem.tsx +6 -23
  40. package/src/components/Navbar/NavbarItem.tsx +8 -33
  41. package/src/core/types/sidebar.ts +1 -0
  42. package/src/core/utils/icon-resolver.ts +1 -4
  43. package/src/ext/configure.ts +1 -1
  44. package/src/icons/CDNIcon/CDNIcon.tsx +2 -2
  45. package/src/icons/UniversalIcon/UniversalIcon.tsx +69 -0
  46. package/src/index.ts +1 -0
  47. package/src/layouts/CodeWalkthroughLayout.tsx +1 -1
  48. package/src/markdoc/components/Cards/CardIcon.tsx +6 -21
  49. package/src/markdoc/components/Tabs/Tab.tsx +6 -1
  50. package/src/markdoc/components/Tabs/Tabs.tsx +1 -1
  51. package/src/markdoc/components/Tabs/variables.ts +2 -0
  52. package/src/markdoc/tags/card.ts +1 -1
  53. package/src/markdoc/tags/tab.ts +1 -0
@@ -1,7 +1,7 @@
1
1
  import { JSX } from 'react';
2
2
  import { CatalogEntityConfig } from '@redocly/config';
3
3
  import { BffCatalogEntityList } from '../../core/types';
4
- import { CatalogSwitcherItem } from '../../core';
4
+ import { CatalogSwitcherItem } from '../../core/types/catalog';
5
5
  export type CatalogProps = {
6
6
  catalogConfig: CatalogEntityConfig;
7
7
  entitiesTypes: string[];
@@ -13,13 +13,13 @@ const ArrowRightIcon_1 = require("../../../icons/ArrowRightIcon/ArrowRightIcon")
13
13
  const ArrowUpRightIcon_1 = require("../../../icons/ArrowUpRightIcon/ArrowUpRightIcon");
14
14
  const CatalogTagsWithTooltip_1 = require("../../../components/Catalog/CatalogTagsWithTooltip");
15
15
  const CatalogEntityIcon_1 = require("../../../components/Catalog/CatalogEntityIcon");
16
- const core_1 = require("../../../core");
16
+ const utils_1 = require("../../../core/utils");
17
17
  const Tooltip_1 = require("../../../components/Tooltip/Tooltip");
18
18
  function CatalogCard({ entity, catalogConfig }) {
19
19
  var _a, _b, _c;
20
20
  const { useTelemetry } = (0, hooks_1.useThemeHooks)();
21
21
  const telemetry = useTelemetry();
22
- const pathPrefix = (0, core_1.getPathPrefix)();
22
+ const pathPrefix = (0, utils_1.getPathPrefix)();
23
23
  return (react_1.default.createElement(CatalogCardWrapper, { "data-component-name": "Catalog/CatalogCardView/CatalogCard", onClick: () => {
24
24
  window.location.assign(`${pathPrefix}/catalogs/${catalogConfig.slug}/entities/${entity.key}`);
25
25
  telemetry.send({ type: 'catalog_item.clicked' });
@@ -1,6 +1,6 @@
1
1
  import { JSX } from 'react';
2
2
  import { Select } from '../../components/Select/Select';
3
- import { CatalogSwitcherItem, SortOption } from '../../core';
3
+ import { CatalogSwitcherItem, SortOption } from '../../core/types/catalog';
4
4
  export type CatalogSelectorProps = {
5
5
  catalogSwitcherItems: CatalogSwitcherItem[];
6
6
  setSearchQuery: (query: string) => void;
@@ -9,13 +9,14 @@ const react_1 = __importDefault(require("react"));
9
9
  const styled_components_1 = __importDefault(require("styled-components"));
10
10
  const react_router_dom_1 = require("react-router-dom");
11
11
  const Select_1 = require("../../components/Select/Select");
12
- const core_1 = require("../../core");
12
+ const utils_1 = require("../../core/utils");
13
+ const hooks_1 = require("../../core/hooks");
13
14
  const ChevronDownIcon_1 = require("../../icons/ChevronDownIcon/ChevronDownIcon");
14
15
  function CatalogSelector({ catalogSwitcherItems, setSearchQuery, setSortOption, }) {
15
- const { useTranslate } = (0, core_1.useThemeHooks)();
16
+ const { useTranslate } = (0, hooks_1.useThemeHooks)();
16
17
  const { translate } = useTranslate();
17
18
  const navigate = (0, react_router_dom_1.useNavigate)();
18
- const pathPrefix = (0, core_1.getPathPrefix)();
19
+ const pathPrefix = (0, utils_1.getPathPrefix)();
19
20
  const options = catalogSwitcherItems.map((item) => ({
20
21
  value: item.slug,
21
22
  element: translate(item.labelTranslationKey),
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { CatalogViewMode } from '../../core';
2
+ import { CatalogViewMode } from '../../core/types/catalog';
3
3
  export type CatalogViewModeToggleProps = {
4
4
  viewMode: CatalogViewMode;
5
5
  onViewModeChange: (mode: CatalogViewMode) => void;
@@ -78,6 +78,7 @@ const DropdownWrapper = styled_components_1.default.div `
78
78
  text-decoration: none;
79
79
  `;
80
80
  const ChildrenWrapper = styled_components_1.default.div `
81
+ margin-top: var(--dropdown-menu-margin-top);
81
82
  position: absolute;
82
83
  top: ${({ placement }) => (placement === 'top' ? 'auto' : '100%')};
83
84
  bottom: ${({ placement }) => (placement === 'top' ? '100%' : 'auto')};
@@ -11,6 +11,7 @@ exports.dropdown = (0, styled_components_1.css) `
11
11
  --dropdown-menu-line-height: var(--line-height-base); // @presenter LineHeight
12
12
  --dropdown-menu-text-color: var(--text-color-secondary); // @presenter Color
13
13
 
14
+ --dropdown-menu-margin-top: var(--spacing-xxs);
14
15
  --dropdown-menu-min-width: 100px;
15
16
  --dropdown-menu-max-width: 424px;
16
17
  --dropdown-menu-max-height: 300px;
@@ -9,11 +9,9 @@ const styled_components_1 = __importDefault(require("styled-components"));
9
9
  const hooks_1 = require("../../core/hooks");
10
10
  const LaunchIcon_1 = require("../../icons/LaunchIcon/LaunchIcon");
11
11
  const Link_1 = require("../../components/Link/Link");
12
- const Image_1 = require("../../components/Image/Image");
13
12
  const utils_1 = require("../../core/utils");
14
- const CDNIcon_1 = require("../../icons/CDNIcon/CDNIcon");
13
+ const UniversalIcon_1 = require("../../icons/UniversalIcon/UniversalIcon");
15
14
  function FooterItem({ item, iconsOnly, className }) {
16
- var _a, _b;
17
15
  const { useTranslate, useTelemetry } = (0, hooks_1.useThemeHooks)();
18
16
  const telemetry = useTelemetry();
19
17
  const { translate } = useTranslate();
@@ -22,12 +20,9 @@ function FooterItem({ item, iconsOnly, className }) {
22
20
  }
23
21
  const hasIcon = Boolean(item.icon || item.srcSet);
24
22
  const iconWithoutLabel = Boolean(item.label === item.link && hasIcon);
25
- const resolvedIcon = (0, utils_1.resolveIcon)(item.icon);
26
- const iconComponent = item.srcSet || resolvedIcon.type === 'link' ? (react_1.default.createElement(Image_1.Image, { src: item.icon, srcSet: item.srcSet, alt: item.label && item.label !== item.link
27
- ? `${item.label} icon`
28
- : `${(_b = (_a = (item.icon || item.srcSet)) === null || _a === void 0 ? void 0 : _a.split('/').pop()) === null || _b === void 0 ? void 0 : _b.split('.')[0]} icon` })) : resolvedIcon.type === 'font-awesome' ? (react_1.default.createElement(CDNIcon_1.CDNIcon, { name: resolvedIcon.name, type: resolvedIcon.style })) : null;
29
23
  return (react_1.default.createElement(FooterItemWrapper, { className: className, "data-component-name": "Footer/FooterItem", iconsOnly: iconsOnly, item: item }, item.type === 'separator' ? (react_1.default.createElement(FooterSeparator, { "data-translation-key": item.labelTranslationKey }, translate(item.labelTranslationKey, item.label))) : (react_1.default.createElement(FooterLink, { to: item.link, external: item.external, target: item.target, "data-testid": item.label, onClick: () => telemetry.send({ type: 'footer_item.clicked' }), "data-translation-key": item.labelTranslationKey },
30
- hasIcon ? react_1.default.createElement(FooterLinkIcon, { iconsOnly: iconsOnly }, iconComponent) : null,
24
+ hasIcon ? (react_1.default.createElement(FooterLinkIcon, { iconsOnly: iconsOnly },
25
+ react_1.default.createElement(UniversalIcon_1.UniversalIcon, { icon: item.icon, srcSet: item.srcSet }))) : null,
31
26
  !iconWithoutLabel ? translate(item.labelTranslationKey, item.label) : null,
32
27
  item.external ? react_1.default.createElement(LaunchIcon_1.LaunchIcon, { size: "10px" }) : null))));
33
28
  }
@@ -38,9 +33,9 @@ const FooterSeparator = styled_components_1.default.div `
38
33
  opacity: var(--footer-separator-item-opacity);
39
34
  `;
40
35
  const FooterLinkIcon = styled_components_1.default.div `
36
+ --icon-width: var(--footer-item-icon-width);
37
+ --icon-height: var(--footer-item-icon-height);
41
38
  display: inline-flex;
42
- width: var(--footer-item-icon-width);
43
- height: var(--footer-item-icon-height);
44
39
  margin-right: ${({ iconsOnly }) => (iconsOnly ? '0' : 'var(--footer-item-icon-margin-right)')};
45
40
  vertical-align: middle;
46
41
  border: 1px solid var(--footer-item-icon-border-color);
@@ -28,8 +28,8 @@ exports.footer = (0, styled_components_1.css) `
28
28
  --footer-link-padding-vertical: var(--spacing-xs); // @presenter Spacing
29
29
  --footer-link-padding-horizontal: 0;
30
30
 
31
- --footer-item-icon-width: var(--spacing-lg); // @presenter Spacing
32
- --footer-item-icon-height: var(--spacing-lg); // @presenter Spacing
31
+ --footer-item-icon-width: var(--spacing-base); // @presenter Spacing
32
+ --footer-item-icon-height: var(--spacing-base); // @presenter Spacing
33
33
  --footer-item-icon-margin-right: var(--spacing-sm); // @presenter Spacing
34
34
  --footer-item-icon-border-color: var(--border-color-primary); // @presenter Color
35
35
  --footer-item-icon-border-radius: 12px; // @presenter BorderRadius
@@ -25,7 +25,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.Markdown = void 0;
27
27
  const styled_components_1 = __importStar(require("styled-components"));
28
- const theme_helpers_1 = require("../../core/utils/theme-helpers");
28
+ const utils_1 = require("../../core/utils");
29
29
  const baseTable_1 = require("../../components/Markdown/styles/baseTable");
30
30
  const links_1 = require("../../components/Markdown/styles/links");
31
31
  const headingAnchor_1 = require("../../components/Markdown/styles/headingAnchor");
@@ -130,37 +130,37 @@ exports.Markdown = styled_components_1.default.main.attrs(({ className }) => ({
130
130
  }
131
131
 
132
132
  h1.md {
133
- ${(0, theme_helpers_1.typography)('h1')};
133
+ ${(0, utils_1.typography)('h1')};
134
134
  margin: var(--h1-margin-top) 0 var(--h1-margin-bottom) 0;
135
135
  ${(0, headingAnchor_1.headingAnchorCss)()};
136
136
  }
137
137
 
138
138
  h2.md {
139
- ${(0, theme_helpers_1.typography)('h2')};
139
+ ${(0, utils_1.typography)('h2')};
140
140
  margin: var(--h2-margin-top) 0 var(--h2-margin-bottom) 0;
141
141
  ${(0, headingAnchor_1.headingAnchorCss)()};
142
142
  }
143
143
 
144
144
  h3.md {
145
- ${(0, theme_helpers_1.typography)('h3')};
145
+ ${(0, utils_1.typography)('h3')};
146
146
  margin: var(--h3-margin-top) 0 var(--h3-margin-bottom) 0;
147
147
  ${(0, headingAnchor_1.headingAnchorCss)()};
148
148
  }
149
149
 
150
150
  h4.md {
151
- ${(0, theme_helpers_1.typography)('h4')};
151
+ ${(0, utils_1.typography)('h4')};
152
152
  margin: var(--h4-margin-top) 0 var(--h4-margin-bottom) 0;
153
153
  ${(0, headingAnchor_1.headingAnchorCss)()};
154
154
  }
155
155
 
156
156
  h5.md {
157
- ${(0, theme_helpers_1.typography)('h5')};
157
+ ${(0, utils_1.typography)('h5')};
158
158
  margin: var(--h5-margin-top) 0 var(--h5-margin-bottom) 0;
159
159
  ${(0, headingAnchor_1.headingAnchorCss)()};
160
160
  }
161
161
 
162
162
  h6.md {
163
- ${(0, theme_helpers_1.typography)('h6')};
163
+ ${(0, utils_1.typography)('h6')};
164
164
  margin: var(--h6-margin-top) 0 var(--h6-margin-bottom) 0;
165
165
  ${(0, headingAnchor_1.headingAnchorCss)()};
166
166
  }
@@ -36,7 +36,7 @@ const constants_1 = require("../../core/constants");
36
36
  const utils_1 = require("../../core/utils");
37
37
  const ArrowRightIcon_1 = require("../../icons/ArrowRightIcon/ArrowRightIcon");
38
38
  const Badge_1 = require("../../components/Badge/Badge");
39
- const CDNIcon_1 = require("../../icons/CDNIcon/CDNIcon");
39
+ const UniversalIcon_1 = require("../../icons/UniversalIcon/UniversalIcon");
40
40
  function MenuItem(props) {
41
41
  var _a;
42
42
  const { item, depth, className, onClick } = props;
@@ -74,11 +74,9 @@ function MenuItem(props) {
74
74
  };
75
75
  const chevron = hasChevron ? (isExpanded ? (react_1.default.createElement(ChevronDownIcon_1.ChevronDownIcon, { size: "var(--menu-item-label-chevron-size)", color: "--tree-content-color-default" })) : (react_1.default.createElement(ChevronRightIcon_1.ChevronRightIcon, { size: "var(--menu-item-label-chevron-size)", color: "--tree-content-color-default" }))) : null;
76
76
  const httpColor = item.deprecated ? 'http-deprecated' : item.httpVerb;
77
- const resolvedIcon = (0, utils_1.resolveIcon)(item.icon);
78
- const iconComponent = resolvedIcon.type === 'link' ? (react_1.default.createElement(MenuItemIcon, { src: item.icon })) : resolvedIcon.type === 'font-awesome' ? (react_1.default.createElement(MenuItemCDNIcon, { name: resolvedIcon.name, type: resolvedIcon.style })) : null;
79
77
  const label = item.label && (react_1.default.createElement(MenuItemLabelWrapper, { active: item.active, deprecated: item.deprecated, depth: depth, withChevron: hasChevron, isSeparator: isSeparator, onClick: handleOnClick, onKeyDown: handleExpandOnEnter, ref: labelRef, role: item.link ? 'none' : 'link', tabIndex: !item.link ? 0 : undefined, "data-testid": "menu-item-label" },
80
78
  hasChevron ? react_1.default.createElement(ChevronWrapper, null, chevron) : null,
81
- iconComponent,
79
+ react_1.default.createElement(MenuItemIcon, { icon: item.icon, srcSet: item.srcSet }),
82
80
  react_1.default.createElement(MenuItemLabelTextWrapper, null,
83
81
  react_1.default.createElement(MenuItemLabel, null,
84
82
  react_1.default.createElement("span", null, translate(item.labelTranslationKey, item.label)), (_a = item.badges) === null || _a === void 0 ? void 0 :
@@ -243,17 +241,9 @@ const MenuItemSubLabel = styled_components_1.default.div `
243
241
  font-size: var(--menu-item-sublabel-font-size);
244
242
  font-family: var(--menu-item-sublabel-font-family);
245
243
  `;
246
- const MenuItemIcon = styled_components_1.default.img `
247
- width: var(--menu-item-icon-size);
248
- height: var(--menu-item-icon-size);
249
- margin: var(--menu-item-icon-margin);
250
- border-radius: var(--menu-item-icon-border-radius);
251
- flex-shrink: 0;
252
- overflow: hidden;
253
- `;
254
- const MenuItemCDNIcon = (0, styled_components_1.default)(CDNIcon_1.CDNIcon) `
255
- width: var(--menu-item-icon-size);
256
- height: var(--menu-item-icon-size);
244
+ const MenuItemIcon = (0, styled_components_1.default)(UniversalIcon_1.UniversalIcon) `
245
+ --icon-width: var(--menu-item-icon-size);
246
+ --icon-height: var(--menu-item-icon-size);
257
247
  margin: var(--menu-item-icon-margin);
258
248
  flex-shrink: 0;
259
249
  overflow: hidden;
@@ -1,34 +1,11 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
4
  };
28
5
  Object.defineProperty(exports, "__esModule", { value: true });
29
6
  exports.NavbarItem = NavbarItem;
30
7
  const react_1 = __importDefault(require("react"));
31
- const styled_components_1 = __importStar(require("styled-components"));
8
+ const styled_components_1 = __importDefault(require("styled-components"));
32
9
  const react_router_dom_1 = require("react-router-dom");
33
10
  const DropdownMenu_1 = require("../../components/Dropdown/DropdownMenu");
34
11
  const DropdownMenuItem_1 = require("../../components/Dropdown/DropdownMenuItem");
@@ -37,8 +14,7 @@ const hooks_1 = require("../../core/hooks");
37
14
  const LaunchIcon_1 = require("../../icons/LaunchIcon/LaunchIcon");
38
15
  const Link_1 = require("../../components/Link/Link");
39
16
  const Dropdown_1 = require("../../components/Dropdown/Dropdown");
40
- const CDNIcon_1 = require("../../icons/CDNIcon/CDNIcon");
41
- const utils_2 = require("../../core/utils");
17
+ const UniversalIcon_1 = require("../../icons/UniversalIcon/UniversalIcon");
42
18
  function NavbarItem({ navItem, className }) {
43
19
  const { pathname } = (0, react_router_dom_1.useLocation)();
44
20
  const { useTranslate, useL10nConfig, useTelemetry } = (0, hooks_1.useThemeHooks)();
@@ -51,10 +27,8 @@ function NavbarItem({ navItem, className }) {
51
27
  const normalizedPath = (item.link && item.link !== '/' ? (0, utils_1.removeTrailingSlash)(item.link) : item.link) || '';
52
28
  const isActive = pathname ===
53
29
  (0, utils_1.withPathPrefix)((0, utils_1.getPathnameForLocale)(normalizedPath, defaultLocale, currentLocale, locales));
54
- const resolvedIcon = (0, utils_2.resolveIcon)(item.icon);
55
- const iconComponent = resolvedIcon.type === 'link' ? (react_1.default.createElement(NavbarIcon, { url: item.icon })) : resolvedIcon.type === 'font-awesome' ? (react_1.default.createElement(NavbarCDNIcon, { name: resolvedIcon.name, type: resolvedIcon.style })) : null;
56
30
  const itemContent = (react_1.default.createElement(NavbarMenuItem, { as: item.link ? Link_1.Link : undefined, active: isActive, className: className, onClick: () => telemetry.send({ type: 'navbar.menu_item_clicked', payload: { type: item.type } }), external: item.external, target: item.target, to: item.link },
57
- iconComponent,
31
+ react_1.default.createElement(NavbarIcon, { icon: item.icon, srcSet: item.srcSet }),
58
32
  react_1.default.createElement(NavbarLabel, { "data-translation-key": item.labelTranslationKey }, translate(item.labelTranslationKey, item.label)),
59
33
  item.external ? react_1.default.createElement(ExternalLinkIcon, { size: "10px" }) : null));
60
34
  if (navItem.items) {
@@ -64,7 +38,7 @@ function NavbarItem({ navItem, className }) {
64
38
  if (item.type !== 'link' && item.type !== 'separator')
65
39
  return null;
66
40
  return (react_1.default.createElement(DropdownMenuItem_1.DropdownMenuItem, { key: `${item.label}_${index}`, to: item.link, separator: item.type === 'separator', separatorLine: item.separatorLine, "data-translation-key": item.labelTranslationKey, external: item.external },
67
- react_1.default.createElement(NavbarIcon, { url: item.icon }),
41
+ react_1.default.createElement(NavbarIcon, { icon: item.icon, srcSet: item.srcSet }),
68
42
  react_1.default.createElement(NavbarLabel, { "data-translation-key": item.labelTranslationKey }, translate(item.labelTranslationKey, item.label)),
69
43
  item.external ? react_1.default.createElement(ExternalLinkIcon, { size: "10px" }) : null));
70
44
  });
@@ -104,25 +78,11 @@ const NavbarLabel = styled_components_1.default.span `
104
78
  cursor: pointer;
105
79
  vertical-align: middle;
106
80
  `;
107
- const NavbarIcon = styled_components_1.default.i `
108
- ${({ url }) => url &&
109
- (0, styled_components_1.css) `
110
- background-image: url('${url}');
111
- width: var(--navbar-item-icon-width);
112
- height: var(--navbar-item-icon-height);
113
- display: inline-block;
114
- background-size: contain;
115
- margin-right: var(--navbar-item-icon-margin-right);
116
- vertical-align: middle;
117
- background-position: center;
118
- background-repeat: no-repeat;
119
- `}
120
- `;
121
- const NavbarCDNIcon = (0, styled_components_1.default)(CDNIcon_1.CDNIcon) `
122
- width: var(--navbar-item-icon-width);
123
- height: var(--navbar-item-icon-height);
81
+ const NavbarIcon = (0, styled_components_1.default)(UniversalIcon_1.UniversalIcon) `
82
+ --icon-width: var(--navbar-item-icon-width);
83
+ --icon-height: var(--navbar-item-icon-height);
124
84
  margin-right: var(--navbar-item-icon-margin-right);
125
- }`;
85
+ `;
126
86
  const ExternalLinkIcon = (0, styled_components_1.default)(LaunchIcon_1.LaunchIcon) `
127
87
  margin-left: 5px;
128
88
  `;
@@ -7,6 +7,7 @@ export type SidebarNavItem = {
7
7
  sublabel?: string;
8
8
  subLabelTranslationKey?: string;
9
9
  icon?: string;
10
+ srcSet?: string;
10
11
  link?: string;
11
12
  type?: string;
12
13
  httpVerb?: string;
@@ -24,10 +24,7 @@ function resolveIcon(icon) {
24
24
  return { type: 'invalid', reason: 'Icon must be a non-empty string' };
25
25
  }
26
26
  const trimmed = icon.trim();
27
- const isLink = trimmed.startsWith('/') ||
28
- trimmed.startsWith('./') ||
29
- trimmed.startsWith('../') ||
30
- /^https?:\/\//.test(trimmed);
27
+ const isLink = trimmed.match(/\.(svg|png|jpg|jpeg|gif|ico|webp)$/) || /^https?:\/\//.test(trimmed);
31
28
  if (isLink) {
32
29
  return { type: 'link', value: trimmed };
33
30
  }
@@ -1,4 +1,4 @@
1
- import { UserClaims, OpenAPIServer } from '../index.js';
1
+ import { UserClaims, OpenAPIServer } from '../core/types';
2
2
  export type SecurityDetails = {
3
3
  password?: string;
4
4
  username?: string;
@@ -42,7 +42,7 @@ const Icon = (props) => {
42
42
  exports.CDNIcon = (0, styled_components_1.default)(Icon).attrs(() => ({
43
43
  'data-component-name': 'icons/CDNIcon/CDNIcon',
44
44
  })) `
45
- height: ${({ size }) => size || 'var(--icon-size, 16px)'};
46
- width: ${({ size }) => size || 'var(--icon-size, 16px)'};
45
+ height: ${({ size }) => size || 'var(--icon-height, 16px)'};
46
+ width: ${({ size }) => size || 'var(--icon-width, 16px)'};
47
47
  `;
48
48
  //# sourceMappingURL=CDNIcon.js.map
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+ export interface UniversalIconProps {
3
+ icon: string | React.ReactNode;
4
+ srcSet?: string;
5
+ rawContent?: string;
6
+ size?: string;
7
+ color?: string;
8
+ alt?: string;
9
+ className?: string;
10
+ }
11
+ export declare function UniversalIcon({ icon, srcSet, rawContent, size, color, alt, className, }: UniversalIconProps): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.JSX.Element | null | undefined;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.UniversalIcon = UniversalIcon;
30
+ const React = __importStar(require("react"));
31
+ const styled_components_1 = __importDefault(require("styled-components"));
32
+ const CDNIcon_1 = require("../../icons/CDNIcon/CDNIcon");
33
+ const utils_1 = require("../../core/utils");
34
+ const InlineSvg_1 = require("../../markdoc/components/InlineSvg/InlineSvg");
35
+ const Image_1 = require("../../components/Image/Image");
36
+ function UniversalIcon({ icon, srcSet, rawContent, size, color, alt, className, }) {
37
+ if (srcSet) {
38
+ return React.createElement(Image_1.Image, { srcSet: srcSet, alt: alt, className: className });
39
+ }
40
+ const resolvedIcon = icon && typeof icon === 'string' ? (0, utils_1.resolveIcon)(icon) : null;
41
+ const iconComponent = rawContent ? (React.createElement(IconSvg, { fileRawContent: rawContent, className: className })) : (resolvedIcon === null || resolvedIcon === void 0 ? void 0 : resolvedIcon.type) === 'link' ? (React.createElement(IconImg, { src: resolvedIcon.value, alt: alt, className: className })) : (resolvedIcon === null || resolvedIcon === void 0 ? void 0 : resolvedIcon.type) === 'font-awesome' ? (React.createElement(CDNIcon_1.CDNIcon, { name: resolvedIcon.name, type: resolvedIcon.style, size: size, color: color, className: className })) : (icon);
42
+ return iconComponent;
43
+ }
44
+ const IconImg = styled_components_1.default.img `
45
+ width: var(--icon-width, 16px);
46
+ height: var(--icon-height, 16px);
47
+ display: inline-block;
48
+ object-fit: cover;
49
+ `;
50
+ const IconSvg = (0, styled_components_1.default)(InlineSvg_1.InlineSvg) `
51
+ width: var(--icon-width, 16px);
52
+ height: var(--icon-height, 16px);
53
+ display: inline-block;
54
+
55
+ svg {
56
+ width: 100%;
57
+ height: 100%;
58
+ fill: var(--icon-color, currentColor);
59
+ }
60
+ `;
61
+ //# sourceMappingURL=UniversalIcon.js.map
package/lib/index.d.ts CHANGED
@@ -254,6 +254,7 @@ export * from './icons/RabbitMQIcon/RabbitMQIcon';
254
254
  export * from './icons/CurveAutoColonIcon/CurveAutoColonIcon';
255
255
  export * from './icons/AiStarsIcon/AiStarsIcon';
256
256
  export * from './icons/AiStarsGradientIcon/AiStarsGradientIcon';
257
+ export * from './icons/UniversalIcon/UniversalIcon';
257
258
  export * from './layouts/RootLayout';
258
259
  export * from './layouts/PageLayout';
259
260
  export * from './layouts/NotFound';
package/lib/index.js CHANGED
@@ -307,6 +307,7 @@ __exportStar(require("./icons/RabbitMQIcon/RabbitMQIcon"), exports);
307
307
  __exportStar(require("./icons/CurveAutoColonIcon/CurveAutoColonIcon"), exports);
308
308
  __exportStar(require("./icons/AiStarsIcon/AiStarsIcon"), exports);
309
309
  __exportStar(require("./icons/AiStarsGradientIcon/AiStarsGradientIcon"), exports);
310
+ __exportStar(require("./icons/UniversalIcon/UniversalIcon"), exports);
310
311
  /* Layouts */
311
312
  __exportStar(require("./layouts/RootLayout"), exports);
312
313
  __exportStar(require("./layouts/PageLayout"), exports);
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.CodeWalkthroughLayout = CodeWalkthroughLayout;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const styled_components_1 = __importDefault(require("styled-components"));
9
- const core_1 = require("../core");
9
+ const utils_1 = require("../core/utils");
10
10
  function CodeWalkthroughLayout({ className, children, }) {
11
11
  return (react_1.default.createElement(LayoutWrapper, { "data-component-name": "Layout/CodeWalkthroughLayout", className: className },
12
12
  react_1.default.createElement(ContentWrapper, null, children)));
@@ -61,7 +61,7 @@ const ContentWrapper = styled_components_1.default.section `
61
61
  /* Full-width styling for all .code-walkthroughs */
62
62
  .code-walkthrough {
63
63
  width: 100%;
64
- max-width: ${core_1.breakpoints.max};
64
+ max-width: ${utils_1.breakpoints.max};
65
65
  margin-left: auto;
66
66
  margin-right: auto;
67
67
  }
@@ -6,28 +6,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.CardIcon = CardIcon;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const styled_components_1 = __importDefault(require("styled-components"));
9
- const InlineSvg_1 = require("../../../markdoc/components/InlineSvg/InlineSvg");
9
+ const UniversalIcon_1 = require("../../../icons/UniversalIcon/UniversalIcon");
10
10
  function CardIcon({ variant, src, rawContent, position }) {
11
- return (react_1.default.createElement(CardIconWrapper, { "$variant": variant, "$position": position }, rawContent ? react_1.default.createElement(CardSvg, { fileRawContent: rawContent }) : react_1.default.createElement(CardImg, { src: src })));
11
+ return (react_1.default.createElement(CardIconWrapper, { "$variant": variant, "$position": position },
12
+ react_1.default.createElement(UniversalIcon_1.UniversalIcon, { icon: src, rawContent: rawContent })));
12
13
  }
13
- const CardImg = styled_components_1.default.img `
14
- width: var(--card-icon-width);
15
- height: var(--card-icon-height);
16
- display: inline-block;
17
- object-fit: cover;
18
- `;
19
- const CardSvg = (0, styled_components_1.default)(InlineSvg_1.InlineSvg) `
20
- width: var(--card-icon-width);
21
- height: var(--card-icon-height);
22
- display: inline-block;
23
-
24
- svg {
25
- width: 100%;
26
- height: 100%;
27
- fill: var(--card-icon-color);
28
- }
29
- `;
30
14
  const CardIconWrapper = styled_components_1.default.div `
15
+ --icon-width: var(--card-icon-width);
16
+ --icon-height: var(--card-icon-height);
17
+ --icon-color: var(--card-icon-color);
18
+
31
19
  display: flex;
32
20
  align-self: ${({ $position }) => ($position ? $position : 'auto')};
33
21
  justify-content: center;
@@ -10,6 +10,7 @@ export type TabProps = {
10
10
  onKeyDown: (event: React.KeyboardEvent<HTMLButtonElement>) => void;
11
11
  onClick: () => void;
12
12
  icon?: React.ReactNode;
13
+ iconRawContent?: string;
13
14
  };
14
- export declare function TabComponent({ tabId, label, size, disabled, setRef, onKeyDown, onClick, icon, }: TabProps): JSX.Element;
15
+ export declare function TabComponent({ tabId, label, size, disabled, setRef, onKeyDown, onClick, icon, iconRawContent, }: TabProps): JSX.Element;
15
16
  export declare const Tab: import("styled-components").StyledComponent<typeof TabComponent, any, {}, never>;
@@ -8,17 +8,20 @@ exports.TabComponent = TabComponent;
8
8
  const react_1 = __importDefault(require("react"));
9
9
  const styled_components_1 = __importDefault(require("styled-components"));
10
10
  const TabList_1 = require("../../../markdoc/components/Tabs/TabList");
11
- function TabComponent({ tabId, label, size, disabled, setRef, onKeyDown, onClick, icon, }) {
11
+ const UniversalIcon_1 = require("../../../icons/UniversalIcon/UniversalIcon");
12
+ function TabComponent({ tabId, label, size, disabled, setRef, onKeyDown, onClick, icon, iconRawContent, }) {
12
13
  return (react_1.default.createElement(TabList_1.TabItem, { "data-component-name": "Markdoc/Tabs/Tab", size: size, tabIndex: 0 },
13
14
  react_1.default.createElement(TabList_1.TabButtonLink, { id: `tab-${tabId}`, role: "tab", "aria-selected": "false", "aria-controls": `panel-${tabId}`, tabIndex: -1, size: size, disabled: disabled, ref: setRef, onKeyDown: onKeyDown, onClick: onClick },
14
15
  react_1.default.createElement(LabelWrapper, null,
15
- icon,
16
+ react_1.default.createElement(UniversalIcon_1.UniversalIcon, { icon: icon, rawContent: iconRawContent }),
16
17
  label))));
17
18
  }
18
19
  const LabelWrapper = styled_components_1.default.div `
19
20
  display: flex;
20
21
  align-items: center;
21
22
  gap: 4px;
23
+ --icon-width: var(--md-tabs-icon-size);
24
+ --icon-height: var(--md-tabs-icon-size);
22
25
  `;
23
26
  exports.Tab = (0, styled_components_1.default)(TabComponent) ``;
24
27
  //# sourceMappingURL=Tab.js.map
@@ -9,7 +9,7 @@ export type TabItemProps = {
9
9
  disable?: boolean;
10
10
  onClick?: () => void;
11
11
  children: React.ReactNode;
12
- icon?: React.ReactNode;
12
+ icon?: React.ReactNode | string;
13
13
  };
14
14
  type TabsProps = {
15
15
  children: React.ReactElement<TabItemProps>[];
@@ -98,6 +98,8 @@ exports.markdownTabs = (0, styled_components_1.css) `
98
98
 
99
99
  --md-tabs-tab-focused-padding: var(--spacing-unit); // @presenter Spacing
100
100
 
101
+ --md-tabs-icon-size: 16px; // @presenter Spacing
102
+
101
103
  // @tokens End
102
104
  `;
103
105
  //# sourceMappingURL=variables.js.map
@@ -14,7 +14,7 @@ exports.card = {
14
14
  type: String,
15
15
  resolver: 'link',
16
16
  },
17
- icon: { type: String, resolver: 'inlineSvgOrImageLink' },
17
+ icon: { type: String, resolver: 'relativeOrCdnIcon' },
18
18
  lineClamp: {
19
19
  type: Number,
20
20
  },
@@ -6,6 +6,7 @@ exports.tab = {
6
6
  attributes: {
7
7
  label: { type: String, required: true },
8
8
  disable: { type: Boolean, default: false },
9
+ icon: { type: String, resolver: 'relativeOrCdnIcon' },
9
10
  },
10
11
  render: 'div',
11
12
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.56.0-next.13",
3
+ "version": "0.56.0-next.14",
4
4
  "description": "Shared UI components lib",
5
5
  "keywords": [
6
6
  "theme",
@@ -8,7 +8,7 @@ import { useThemeHooks } from '@redocly/theme/core/hooks';
8
8
  import { H3 } from '@redocly/theme/components/Typography/H3';
9
9
  import { FilterContent } from '@redocly/theme/components/Filter/FilterContent';
10
10
  import { Sidebar, SidebarHeader } from '@redocly/theme/components/Sidebar/Sidebar';
11
- import { CatalogSwitcherItem } from '@redocly/theme/core';
11
+ import { CatalogSwitcherItem } from '@redocly/theme/core/types/catalog';
12
12
  import { CatalogSelector } from '@redocly/theme/components/Catalog/CatalogSelector';
13
13
  import { SidebarActions } from '@redocly/theme/components/SidebarActions/SidebarActions';
14
14
  import { CounterTag } from '@redocly/theme/components/Tags/CounterTag';
@@ -10,7 +10,7 @@ import { ArrowRightIcon } from '@redocly/theme/icons/ArrowRightIcon/ArrowRightIc
10
10
  import { ArrowUpRightIcon } from '@redocly/theme/icons/ArrowUpRightIcon/ArrowUpRightIcon';
11
11
  import { CatalogTagsWithTooltip } from '@redocly/theme/components/Catalog/CatalogTagsWithTooltip';
12
12
  import { CatalogEntityIcon } from '@redocly/theme/components/Catalog/CatalogEntityIcon';
13
- import { getPathPrefix } from '@redocly/theme/core';
13
+ import { getPathPrefix } from '@redocly/theme/core/utils';
14
14
  import { TooltipComponent } from '@redocly/theme/components/Tooltip/Tooltip';
15
15
 
16
16
  export type CatalogCardProps = {
@@ -3,7 +3,9 @@ import styled from 'styled-components';
3
3
  import { useNavigate } from 'react-router-dom';
4
4
 
5
5
  import { Select } from '@redocly/theme/components/Select/Select';
6
- import { CatalogSwitcherItem, getPathPrefix, SortOption, useThemeHooks } from '@redocly/theme/core';
6
+ import { CatalogSwitcherItem, SortOption } from '@redocly/theme/core/types/catalog';
7
+ import { getPathPrefix } from '@redocly/theme/core/utils';
8
+ import { useThemeHooks } from '@redocly/theme/core/hooks';
7
9
  import { ChevronDownIcon } from '@redocly/theme/icons/ChevronDownIcon/ChevronDownIcon';
8
10
 
9
11
  export type CatalogSelectorProps = {
@@ -3,7 +3,7 @@ import styled from 'styled-components';
3
3
 
4
4
  import { TableIcon } from '@redocly/theme/icons/TableIcon/TableIcon';
5
5
  import { CardsIcon } from '@redocly/theme/icons/CardsIcon/CardsIcon';
6
- import { CatalogViewMode } from '@redocly/theme/core';
6
+ import { CatalogViewMode } from '@redocly/theme/core/types/catalog';
7
7
 
8
8
  function useViewMode(initialViewMode: CatalogViewMode) {
9
9
  const [viewMode, setViewMode] = useState<CatalogViewMode | null>(null);
@@ -126,6 +126,7 @@ const DropdownWrapper = styled.div`
126
126
  `;
127
127
 
128
128
  const ChildrenWrapper = styled.div<{ placement?: string; alignment?: string; isOpen?: boolean }>`
129
+ margin-top: var(--dropdown-menu-margin-top);
129
130
  position: absolute;
130
131
  top: ${({ placement }) => (placement === 'top' ? 'auto' : '100%')};
131
132
  bottom: ${({ placement }) => (placement === 'top' ? '100%' : 'auto')};
@@ -9,6 +9,7 @@ export const dropdown = css`
9
9
  --dropdown-menu-line-height: var(--line-height-base); // @presenter LineHeight
10
10
  --dropdown-menu-text-color: var(--text-color-secondary); // @presenter Color
11
11
 
12
+ --dropdown-menu-margin-top: var(--spacing-xxs);
12
13
  --dropdown-menu-min-width: 100px;
13
14
  --dropdown-menu-max-width: 424px;
14
15
  --dropdown-menu-max-height: 300px;
@@ -7,9 +7,8 @@ import type { ResolvedNavItem } from '@redocly/config';
7
7
  import { useThemeHooks } from '@redocly/theme/core/hooks';
8
8
  import { LaunchIcon } from '@redocly/theme/icons/LaunchIcon/LaunchIcon';
9
9
  import { Link } from '@redocly/theme/components/Link/Link';
10
- import { Image } from '@redocly/theme/components/Image/Image';
11
- import { breakpoints, resolveIcon } from '@redocly/theme/core/utils';
12
- import { CDNIcon } from '@redocly/theme/icons/CDNIcon/CDNIcon';
10
+ import { breakpoints } from '@redocly/theme/core/utils';
11
+ import { UniversalIcon } from '@redocly/theme/icons/UniversalIcon/UniversalIcon';
13
12
 
14
13
  export type FooterItemProps = {
15
14
  item: ResolvedNavItem;
@@ -27,21 +26,7 @@ export function FooterItem({ item, iconsOnly, className }: FooterItemProps): JSX
27
26
  }
28
27
  const hasIcon = Boolean(item.icon || item.srcSet);
29
28
  const iconWithoutLabel = Boolean(item.label === item.link && hasIcon);
30
- const resolvedIcon = resolveIcon(item.icon);
31
- const iconComponent =
32
- item.srcSet || resolvedIcon.type === 'link' ? (
33
- <Image
34
- src={item.icon}
35
- srcSet={item.srcSet}
36
- alt={
37
- item.label && item.label !== item.link
38
- ? `${item.label} icon`
39
- : `${(item.icon || item.srcSet)?.split('/').pop()?.split('.')[0]} icon`
40
- }
41
- />
42
- ) : resolvedIcon.type === 'font-awesome' ? (
43
- <CDNIcon name={resolvedIcon.name} type={resolvedIcon.style} />
44
- ) : null;
29
+
45
30
  return (
46
31
  <FooterItemWrapper
47
32
  className={className}
@@ -62,7 +47,11 @@ export function FooterItem({ item, iconsOnly, className }: FooterItemProps): JSX
62
47
  onClick={() => telemetry.send({ type: 'footer_item.clicked' })}
63
48
  data-translation-key={item.labelTranslationKey}
64
49
  >
65
- {hasIcon ? <FooterLinkIcon iconsOnly={iconsOnly}>{iconComponent}</FooterLinkIcon> : null}
50
+ {hasIcon ? (
51
+ <FooterLinkIcon iconsOnly={iconsOnly}>
52
+ <UniversalIcon icon={item.icon} srcSet={item.srcSet} />
53
+ </FooterLinkIcon>
54
+ ) : null}
66
55
  {!iconWithoutLabel ? translate(item.labelTranslationKey, item.label) : null}
67
56
  {item.external ? <LaunchIcon size="10px" /> : null}
68
57
  </FooterLink>
@@ -79,9 +68,9 @@ const FooterSeparator = styled.div`
79
68
  `;
80
69
 
81
70
  const FooterLinkIcon = styled.div<{ iconsOnly?: boolean }>`
71
+ --icon-width: var(--footer-item-icon-width);
72
+ --icon-height: var(--footer-item-icon-height);
82
73
  display: inline-flex;
83
- width: var(--footer-item-icon-width);
84
- height: var(--footer-item-icon-height);
85
74
  margin-right: ${({ iconsOnly }) => (iconsOnly ? '0' : 'var(--footer-item-icon-margin-right)')};
86
75
  vertical-align: middle;
87
76
  border: 1px solid var(--footer-item-icon-border-color);
@@ -26,8 +26,8 @@ export const footer = css`
26
26
  --footer-link-padding-vertical: var(--spacing-xs); // @presenter Spacing
27
27
  --footer-link-padding-horizontal: 0;
28
28
 
29
- --footer-item-icon-width: var(--spacing-lg); // @presenter Spacing
30
- --footer-item-icon-height: var(--spacing-lg); // @presenter Spacing
29
+ --footer-item-icon-width: var(--spacing-base); // @presenter Spacing
30
+ --footer-item-icon-height: var(--spacing-base); // @presenter Spacing
31
31
  --footer-item-icon-margin-right: var(--spacing-sm); // @presenter Spacing
32
32
  --footer-item-icon-border-color: var(--border-color-primary); // @presenter Color
33
33
  --footer-item-icon-border-radius: 12px; // @presenter BorderRadius
@@ -2,7 +2,7 @@ import styled, { css } from 'styled-components';
2
2
 
3
3
  import type { PropsWithChildren } from 'react';
4
4
 
5
- import { typography } from '@redocly/theme/core/utils/theme-helpers';
5
+ import { typography } from '@redocly/theme/core/utils';
6
6
  import { markdownBaseTableCss } from '@redocly/theme/components/Markdown/styles/baseTable';
7
7
  import { markdownLinksCss } from '@redocly/theme/components/Markdown/styles/links';
8
8
  import { headingAnchorCss } from '@redocly/theme/components/Markdown/styles/headingAnchor';
@@ -11,10 +11,10 @@ import { ChevronDownIcon } from '@redocly/theme/icons/ChevronDownIcon/ChevronDow
11
11
  import { ChevronRightIcon } from '@redocly/theme/icons/ChevronRightIcon/ChevronRightIcon';
12
12
  import { HttpTag } from '@redocly/theme/components/Tags/HttpTag';
13
13
  import { MenuItemType } from '@redocly/theme/core/constants';
14
- import { getMenuItemType, resolveIcon } from '@redocly/theme/core/utils';
14
+ import { getMenuItemType } from '@redocly/theme/core/utils';
15
15
  import { ArrowRightIcon } from '@redocly/theme/icons/ArrowRightIcon/ArrowRightIcon';
16
16
  import { Badge } from '@redocly/theme/components/Badge/Badge';
17
- import { CDNIcon } from '@redocly/theme/icons/CDNIcon/CDNIcon';
17
+ import { UniversalIcon } from '@redocly/theme/icons/UniversalIcon/UniversalIcon';
18
18
 
19
19
  export function MenuItem(props: React.PropsWithChildren<MenuItemProps>): JSX.Element {
20
20
  const { item, depth, className, onClick } = props;
@@ -72,14 +72,6 @@ export function MenuItem(props: React.PropsWithChildren<MenuItemProps>): JSX.Ele
72
72
 
73
73
  const httpColor = item.deprecated ? 'http-deprecated' : item.httpVerb;
74
74
 
75
- const resolvedIcon = resolveIcon(item.icon);
76
- const iconComponent =
77
- resolvedIcon.type === 'link' ? (
78
- <MenuItemIcon src={item.icon} />
79
- ) : resolvedIcon.type === 'font-awesome' ? (
80
- <MenuItemCDNIcon name={resolvedIcon.name} type={resolvedIcon.style} />
81
- ) : null;
82
-
83
75
  const label = item.label && (
84
76
  <MenuItemLabelWrapper
85
77
  active={item.active}
@@ -95,7 +87,7 @@ export function MenuItem(props: React.PropsWithChildren<MenuItemProps>): JSX.Ele
95
87
  data-testid="menu-item-label"
96
88
  >
97
89
  {hasChevron ? <ChevronWrapper>{chevron}</ChevronWrapper> : null}
98
- {iconComponent}
90
+ <MenuItemIcon icon={item.icon} srcSet={item.srcSet} />
99
91
  <MenuItemLabelTextWrapper>
100
92
  <MenuItemLabel>
101
93
  <span>{translate(item.labelTranslationKey, item.label)}</span>
@@ -334,18 +326,9 @@ const MenuItemSubLabel = styled.div`
334
326
  font-family: var(--menu-item-sublabel-font-family);
335
327
  `;
336
328
 
337
- const MenuItemIcon = styled.img`
338
- width: var(--menu-item-icon-size);
339
- height: var(--menu-item-icon-size);
340
- margin: var(--menu-item-icon-margin);
341
- border-radius: var(--menu-item-icon-border-radius);
342
- flex-shrink: 0;
343
- overflow: hidden;
344
- `;
345
-
346
- const MenuItemCDNIcon = styled(CDNIcon)`
347
- width: var(--menu-item-icon-size);
348
- height: var(--menu-item-icon-size);
329
+ const MenuItemIcon = styled(UniversalIcon)`
330
+ --icon-width: var(--menu-item-icon-size);
331
+ --icon-height: var(--menu-item-icon-size);
349
332
  margin: var(--menu-item-icon-margin);
350
333
  flex-shrink: 0;
351
334
  overflow: hidden;
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import styled, { css } from 'styled-components';
2
+ import styled from 'styled-components';
3
3
  import { useLocation } from 'react-router-dom';
4
4
 
5
5
  import type { JSX } from 'react';
@@ -17,8 +17,7 @@ import { useThemeHooks } from '@redocly/theme/core/hooks';
17
17
  import { LaunchIcon } from '@redocly/theme/icons/LaunchIcon/LaunchIcon';
18
18
  import { Link } from '@redocly/theme/components/Link/Link';
19
19
  import { Dropdown } from '@redocly/theme/components/Dropdown/Dropdown';
20
- import { CDNIcon } from '@redocly/theme/icons/CDNIcon/CDNIcon';
21
- import { resolveIcon } from '@redocly/theme/core/utils';
20
+ import { UniversalIcon } from '@redocly/theme/icons/UniversalIcon/UniversalIcon';
22
21
 
23
22
  export type NavbarItemProps = {
24
23
  navItem: ResolvedNavItem;
@@ -42,14 +41,6 @@ export function NavbarItem({ navItem, className }: NavbarItemProps): JSX.Element
42
41
  pathname ===
43
42
  withPathPrefix(getPathnameForLocale(normalizedPath, defaultLocale, currentLocale, locales));
44
43
 
45
- const resolvedIcon = resolveIcon(item.icon);
46
- const iconComponent =
47
- resolvedIcon.type === 'link' ? (
48
- <NavbarIcon url={item.icon} />
49
- ) : resolvedIcon.type === 'font-awesome' ? (
50
- <NavbarCDNIcon name={resolvedIcon.name} type={resolvedIcon.style} />
51
- ) : null;
52
-
53
44
  const itemContent = (
54
45
  <NavbarMenuItem
55
46
  as={item.link ? Link : undefined}
@@ -62,7 +53,7 @@ export function NavbarItem({ navItem, className }: NavbarItemProps): JSX.Element
62
53
  target={item.target}
63
54
  to={item.link}
64
55
  >
65
- {iconComponent}
56
+ <NavbarIcon icon={item.icon} srcSet={item.srcSet} />
66
57
  <NavbarLabel data-translation-key={item.labelTranslationKey}>
67
58
  {translate(item.labelTranslationKey, item.label)}
68
59
  </NavbarLabel>
@@ -84,7 +75,7 @@ export function NavbarItem({ navItem, className }: NavbarItemProps): JSX.Element
84
75
  data-translation-key={item.labelTranslationKey}
85
76
  external={item.external}
86
77
  >
87
- <NavbarIcon url={item.icon} />
78
+ <NavbarIcon icon={item.icon} srcSet={item.srcSet} />
88
79
  <NavbarLabel data-translation-key={item.labelTranslationKey}>
89
80
  {translate(item.labelTranslationKey, item.label)}
90
81
  </NavbarLabel>
@@ -142,27 +133,11 @@ const NavbarLabel = styled.span`
142
133
  vertical-align: middle;
143
134
  `;
144
135
 
145
- const NavbarIcon = styled.i<{ url?: string }>`
146
- ${({ url }) =>
147
- url &&
148
- css`
149
- background-image: url('${url}');
150
- width: var(--navbar-item-icon-width);
151
- height: var(--navbar-item-icon-height);
152
- display: inline-block;
153
- background-size: contain;
154
- margin-right: var(--navbar-item-icon-margin-right);
155
- vertical-align: middle;
156
- background-position: center;
157
- background-repeat: no-repeat;
158
- `}
159
- `;
160
-
161
- const NavbarCDNIcon = styled(CDNIcon)`
162
- width: var(--navbar-item-icon-width);
163
- height: var(--navbar-item-icon-height);
136
+ const NavbarIcon = styled(UniversalIcon)`
137
+ --icon-width: var(--navbar-item-icon-width);
138
+ --icon-height: var(--navbar-item-icon-height);
164
139
  margin-right: var(--navbar-item-icon-margin-right);
165
- }`;
140
+ `;
166
141
 
167
142
  const ExternalLinkIcon = styled(LaunchIcon)`
168
143
  margin-left: 5px;
@@ -14,6 +14,7 @@ export type SidebarNavItem = {
14
14
  sublabel?: string;
15
15
  subLabelTranslationKey?: string;
16
16
  icon?: string;
17
+ srcSet?: string;
17
18
  link?: string;
18
19
  type?: string;
19
20
  httpVerb?: string;
@@ -29,10 +29,7 @@ export function resolveIcon(icon?: string): ResolvedIcon {
29
29
 
30
30
  const trimmed = icon.trim();
31
31
  const isLink =
32
- trimmed.startsWith('/') ||
33
- trimmed.startsWith('./') ||
34
- trimmed.startsWith('../') ||
35
- /^https?:\/\//.test(trimmed);
32
+ trimmed.match(/\.(svg|png|jpg|jpeg|gif|ico|webp)$/) || /^https?:\/\//.test(trimmed);
36
33
 
37
34
  if (isLink) {
38
35
  return { type: 'link', value: trimmed };
@@ -1,4 +1,4 @@
1
- import { UserClaims, OpenAPIServer } from '@redocly/theme';
1
+ import { UserClaims, OpenAPIServer } from '@redocly/theme/core/types';
2
2
 
3
3
  export type SecurityDetails = {
4
4
  password?: string;
@@ -42,6 +42,6 @@ const Icon = (props: CDNIconProps) => {
42
42
  export const CDNIcon = styled(Icon).attrs(() => ({
43
43
  'data-component-name': 'icons/CDNIcon/CDNIcon',
44
44
  }))<CDNIconProps>`
45
- height: ${({ size }) => size || 'var(--icon-size, 16px)'};
46
- width: ${({ size }) => size || 'var(--icon-size, 16px)'};
45
+ height: ${({ size }) => size || 'var(--icon-height, 16px)'};
46
+ width: ${({ size }) => size || 'var(--icon-width, 16px)'};
47
47
  `;
@@ -0,0 +1,69 @@
1
+ import * as React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import { CDNIcon } from '@redocly/theme/icons/CDNIcon/CDNIcon';
5
+ import { resolveIcon } from '@redocly/theme/core/utils';
6
+ import { InlineSvg } from '@redocly/theme/markdoc/components/InlineSvg/InlineSvg';
7
+ import { Image } from '@redocly/theme/components/Image/Image';
8
+
9
+ export interface UniversalIconProps {
10
+ icon: string | React.ReactNode;
11
+ srcSet?: string;
12
+ rawContent?: string;
13
+ size?: string;
14
+ color?: string;
15
+ alt?: string;
16
+ className?: string;
17
+ }
18
+
19
+ export function UniversalIcon({
20
+ icon,
21
+ srcSet,
22
+ rawContent,
23
+ size,
24
+ color,
25
+ alt,
26
+ className,
27
+ }: UniversalIconProps) {
28
+ if (srcSet) {
29
+ return <Image srcSet={srcSet} alt={alt} className={className} />;
30
+ }
31
+
32
+ const resolvedIcon = icon && typeof icon === 'string' ? resolveIcon(icon) : null;
33
+ const iconComponent = rawContent ? (
34
+ <IconSvg fileRawContent={rawContent} className={className} />
35
+ ) : resolvedIcon?.type === 'link' ? (
36
+ <IconImg src={resolvedIcon.value} alt={alt} className={className} />
37
+ ) : resolvedIcon?.type === 'font-awesome' ? (
38
+ <CDNIcon
39
+ name={resolvedIcon.name}
40
+ type={resolvedIcon.style}
41
+ size={size}
42
+ color={color}
43
+ className={className}
44
+ />
45
+ ) : (
46
+ icon
47
+ );
48
+
49
+ return iconComponent;
50
+ }
51
+
52
+ const IconImg = styled.img`
53
+ width: var(--icon-width, 16px);
54
+ height: var(--icon-height, 16px);
55
+ display: inline-block;
56
+ object-fit: cover;
57
+ `;
58
+
59
+ const IconSvg = styled(InlineSvg)`
60
+ width: var(--icon-width, 16px);
61
+ height: var(--icon-height, 16px);
62
+ display: inline-block;
63
+
64
+ svg {
65
+ width: 100%;
66
+ height: 100%;
67
+ fill: var(--icon-color, currentColor);
68
+ }
69
+ `;
package/src/index.ts CHANGED
@@ -280,6 +280,7 @@ export * from '@redocly/theme/icons/RabbitMQIcon/RabbitMQIcon';
280
280
  export * from '@redocly/theme/icons/CurveAutoColonIcon/CurveAutoColonIcon';
281
281
  export * from '@redocly/theme/icons/AiStarsIcon/AiStarsIcon';
282
282
  export * from '@redocly/theme/icons/AiStarsGradientIcon/AiStarsGradientIcon';
283
+ export * from '@redocly/theme/icons/UniversalIcon/UniversalIcon';
283
284
  /* Layouts */
284
285
  export * from '@redocly/theme/layouts/RootLayout';
285
286
  export * from '@redocly/theme/layouts/PageLayout';
@@ -3,7 +3,7 @@ import styled from 'styled-components';
3
3
 
4
4
  import type { JSX } from 'react';
5
5
 
6
- import { breakpoints } from '@redocly/theme/core';
6
+ import { breakpoints } from '@redocly/theme/core/utils';
7
7
 
8
8
  export type CodeWalkthroughLayoutProps = React.PropsWithChildren<{
9
9
  className?: string;
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import styled from 'styled-components';
3
3
 
4
- import { InlineSvg } from '@redocly/theme/markdoc/components/InlineSvg/InlineSvg';
4
+ import { UniversalIcon } from '@redocly/theme/icons/UniversalIcon/UniversalIcon';
5
5
 
6
6
  export type CardIconProps = {
7
7
  variant?: string;
@@ -13,31 +13,16 @@ export type CardIconProps = {
13
13
  export function CardIcon({ variant, src, rawContent, position }: CardIconProps) {
14
14
  return (
15
15
  <CardIconWrapper $variant={variant} $position={position}>
16
- {rawContent ? <CardSvg fileRawContent={rawContent} /> : <CardImg src={src} />}
16
+ <UniversalIcon icon={src} rawContent={rawContent} />
17
17
  </CardIconWrapper>
18
18
  );
19
19
  }
20
20
 
21
- const CardImg = styled.img`
22
- width: var(--card-icon-width);
23
- height: var(--card-icon-height);
24
- display: inline-block;
25
- object-fit: cover;
26
- `;
27
-
28
- const CardSvg = styled(InlineSvg)`
29
- width: var(--card-icon-width);
30
- height: var(--card-icon-height);
31
- display: inline-block;
32
-
33
- svg {
34
- width: 100%;
35
- height: 100%;
36
- fill: var(--card-icon-color);
37
- }
38
- `;
39
-
40
21
  const CardIconWrapper = styled.div<{ $variant?: string; $position?: string }>`
22
+ --icon-width: var(--card-icon-width);
23
+ --icon-height: var(--card-icon-height);
24
+ --icon-color: var(--card-icon-color);
25
+
41
26
  display: flex;
42
27
  align-self: ${({ $position }) => ($position ? $position : 'auto')};
43
28
  justify-content: center;
@@ -5,6 +5,7 @@ import type { JSX } from 'react';
5
5
 
6
6
  import { TabsSize } from '@redocly/theme/markdoc/components/Tabs/Tabs';
7
7
  import { TabButtonLink, TabItem } from '@redocly/theme/markdoc/components/Tabs/TabList';
8
+ import { UniversalIcon } from '@redocly/theme/icons/UniversalIcon/UniversalIcon';
8
9
 
9
10
  export type TabProps = {
10
11
  tabId: string;
@@ -15,6 +16,7 @@ export type TabProps = {
15
16
  onKeyDown: (event: React.KeyboardEvent<HTMLButtonElement>) => void;
16
17
  onClick: () => void;
17
18
  icon?: React.ReactNode;
19
+ iconRawContent?: string;
18
20
  };
19
21
 
20
22
  export function TabComponent({
@@ -26,6 +28,7 @@ export function TabComponent({
26
28
  onKeyDown,
27
29
  onClick,
28
30
  icon,
31
+ iconRawContent,
29
32
  }: TabProps): JSX.Element {
30
33
  return (
31
34
  <TabItem data-component-name="Markdoc/Tabs/Tab" size={size} tabIndex={0}>
@@ -42,7 +45,7 @@ export function TabComponent({
42
45
  onClick={onClick}
43
46
  >
44
47
  <LabelWrapper>
45
- {icon}
48
+ <UniversalIcon icon={icon} rawContent={iconRawContent} />
46
49
  {label}
47
50
  </LabelWrapper>
48
51
  </TabButtonLink>
@@ -54,6 +57,8 @@ const LabelWrapper = styled.div`
54
57
  display: flex;
55
58
  align-items: center;
56
59
  gap: 4px;
60
+ --icon-width: var(--md-tabs-icon-size);
61
+ --icon-height: var(--md-tabs-icon-size);
57
62
  `;
58
63
 
59
64
  export const Tab = styled(TabComponent)``;
@@ -16,7 +16,7 @@ export type TabItemProps = {
16
16
  disable?: boolean;
17
17
  onClick?: () => void;
18
18
  children: React.ReactNode;
19
- icon?: React.ReactNode;
19
+ icon?: React.ReactNode | string;
20
20
  };
21
21
 
22
22
  type TabsProps = {
@@ -96,5 +96,7 @@ export const markdownTabs = css`
96
96
 
97
97
  --md-tabs-tab-focused-padding: var(--spacing-unit); // @presenter Spacing
98
98
 
99
+ --md-tabs-icon-size: 16px; // @presenter Spacing
100
+
99
101
  // @tokens End
100
102
  `;
@@ -14,7 +14,7 @@ export const card: MarkdocSchemaWrapper = {
14
14
  type: String,
15
15
  resolver: 'link',
16
16
  },
17
- icon: { type: String, resolver: 'inlineSvgOrImageLink' },
17
+ icon: { type: String, resolver: 'relativeOrCdnIcon' },
18
18
  lineClamp: {
19
19
  type: Number,
20
20
  },
@@ -5,6 +5,7 @@ export const tab: MarkdocSchemaWrapper = {
5
5
  attributes: {
6
6
  label: { type: String, required: true },
7
7
  disable: { type: Boolean, default: false },
8
+ icon: { type: String, resolver: 'relativeOrCdnIcon' },
8
9
  },
9
10
  render: 'div',
10
11
  },