@stoplight/elements-core 7.9.0 → 7.11.0

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.
@@ -4,6 +4,7 @@ import * as React from 'react';
4
4
  import { ParsedNode } from '../../types';
5
5
  import { ReferenceResolver } from '../../utils/ref-resolving/ReferenceResolver';
6
6
  import { ExportButtonProps } from './HttpService/ExportButton';
7
+ declare type NodeUnsupportedFn = (err: 'dataEmpty' | 'invalidType' | Error) => void;
7
8
  interface BaseDocsProps {
8
9
  className?: string;
9
10
  uri?: string;
@@ -25,6 +26,7 @@ interface BaseDocsProps {
25
26
  compact?: number | boolean;
26
27
  };
27
28
  nodeHasChanged?: NodeHasChangedFn<React.ReactNode>;
29
+ nodeUnsupported?: NodeUnsupportedFn;
28
30
  }
29
31
  export interface DocsProps extends BaseDocsProps {
30
32
  nodeType: NodeType;
@@ -39,5 +41,5 @@ export declare const Docs: React.NamedExoticComponent<DocsProps>;
39
41
  export interface ParsedDocsProps extends BaseDocsProps {
40
42
  node: ParsedNode;
41
43
  }
42
- export declare const ParsedDocs: ({ node, ...commonProps }: ParsedDocsProps) => JSX.Element | null;
44
+ export declare const ParsedDocs: ({ node, nodeUnsupported, ...commonProps }: ParsedDocsProps) => JSX.Element | null;
43
45
  export { DocsSkeleton } from './Skeleton';
@@ -3,9 +3,10 @@ interface ResponsesProps {
3
3
  responses: IHttpOperationResponse[];
4
4
  onMediaTypeChange(mediaType: string): void;
5
5
  onStatusCodeChange(statusCode: string): void;
6
+ isCompact?: boolean;
6
7
  }
7
8
  export declare const Responses: {
8
- ({ responses: unsortedResponses, onStatusCodeChange, onMediaTypeChange }: ResponsesProps): JSX.Element | null;
9
+ ({ responses: unsortedResponses, onStatusCodeChange, onMediaTypeChange, isCompact, }: ResponsesProps): JSX.Element | null;
9
10
  displayName: string;
10
11
  };
11
12
  export {};
package/index.esm.js CHANGED
@@ -7,7 +7,7 @@ import isArray from 'lodash/isArray.js';
7
7
  import isPlainObject from 'lodash/isPlainObject.js';
8
8
  import { NodeType, HttpParamStyles } from '@stoplight/types';
9
9
  import { parse } from '@stoplight/yaml';
10
- import { isArray as isArray$1, Box, Panel, CopyButton, Menu, Button, Text, Flex, Input, Icon, Select, FieldButton, Image, Link, useThemeIsDark, HStack, VStack, InvertTheme, Tooltip, Badge, LinkHeading as LinkHeading$1, NodeAnnotation, Tabs, TabList, Tab, TabPanels, TabPanel, Heading, useClipboard, useBreakpoints, useMosaicContext, Provider as Provider$1 } from '@stoplight/mosaic';
10
+ import { isArray as isArray$1, Box, useBreakpoints, Panel, CopyButton, Menu, Button, Text, Flex, Input, Icon, Select, FieldButton, Image, Link, useThemeIsDark, HStack, VStack, InvertTheme, Tooltip, Badge, LinkHeading as LinkHeading$1, NodeAnnotation, useModalState, Modal, ListBox, ListBoxItem, TabList, Tab, Tabs, TabPanels, TabPanel, Heading, useClipboard, useMosaicContext, Provider as Provider$1 } from '@stoplight/mosaic';
11
11
  import isObject from 'lodash/isObject.js';
12
12
  import { withErrorBoundary } from '@stoplight/react-error-boundary';
13
13
  import { useLocation, BrowserRouter, MemoryRouter, HashRouter, StaticRouter, Route } from 'react-router-dom';
@@ -486,6 +486,22 @@ function useChosenServerUrl(chosenServerUrl) {
486
486
  };
487
487
  }
488
488
 
489
+ const getBreakpoints = (compact) => {
490
+ if (!compact)
491
+ return undefined;
492
+ if (typeof compact === 'number') {
493
+ return [
494
+ ['compact', compact],
495
+ ['regular', Infinity],
496
+ ];
497
+ }
498
+ return [['compact', Infinity]];
499
+ };
500
+ function useIsCompact(layoutOptions) {
501
+ const { ref, breakpoint } = useBreakpoints(getBreakpoints(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.compact));
502
+ return { ref: ref, isCompact: breakpoint === 'compact' };
503
+ }
504
+
489
505
  const chosenServerAtom = atom(undefined);
490
506
 
491
507
  function isValidServer(server) {
@@ -2410,20 +2426,41 @@ const SecuritySchemes$1 = ({ schemes }) => {
2410
2426
  React.createElement(NodeAnnotation, { change: nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: scheme.id }) }))))));
2411
2427
  };
2412
2428
 
2413
- const Responses = ({ responses: unsortedResponses, onStatusCodeChange, onMediaTypeChange }) => {
2429
+ const Responses = ({ responses: unsortedResponses, onStatusCodeChange, onMediaTypeChange, isCompact, }) => {
2414
2430
  var _a, _b;
2415
2431
  const responses = sortBy(uniqBy(unsortedResponses, r => r.code), r => r.code);
2416
2432
  const [activeResponseId, setActiveResponseId] = React.useState((_b = (_a = responses[0]) === null || _a === void 0 ? void 0 : _a.code) !== null && _b !== void 0 ? _b : '');
2433
+ const { isOpen, open, close } = useModalState();
2434
+ const onSelectionChange = React.useCallback(keys => {
2435
+ const selectedId = keys.values().next().value;
2436
+ const selectedResponse = responses === null || responses === void 0 ? void 0 : responses.find(response => response.id === selectedId);
2437
+ if (selectedResponse) {
2438
+ setActiveResponseId(selectedResponse.code);
2439
+ close();
2440
+ }
2441
+ }, [responses, setActiveResponseId, close]);
2417
2442
  React.useEffect(() => {
2418
2443
  onStatusCodeChange(activeResponseId);
2419
2444
  }, [activeResponseId]);
2420
2445
  if (!responses.length)
2421
2446
  return null;
2447
+ const response = responses.find(r => r.code === activeResponseId) || responses[0];
2448
+ const compactResponses = (React.createElement(React.Fragment, null,
2449
+ React.createElement(Button, { onPress: open, iconRight: React.createElement(Icon, { icon: "chevron-down", color: "var(--color-border-button)" }), style: {
2450
+ color: `var(--color-${codeToIntentVal(activeResponseId)})`,
2451
+ } }, activeResponseId),
2452
+ React.createElement(Modal, { title: "Response Code", isOpen: isOpen, onClose: close, size: "sm", footer: React.createElement(HStack, { justifyContent: "end" },
2453
+ React.createElement(Button, { onPress: close, intent: "default", appearance: "primary" }, "Close")) },
2454
+ React.createElement(ListBox, { "aria-label": "Response Code", overflowY: "auto", m: -5, items: responses, selectionMode: "single", onSelectionChange: onSelectionChange }, (response) => (React.createElement(ListBoxItem, { key: response.id },
2455
+ React.createElement(Box, { p: 3, bg: { hover: 'primary-tint' } },
2456
+ React.createElement(Flex, { w: "2xl", align: "center", justify: "end" },
2457
+ response.code === activeResponseId && React.createElement(Box, { as: Icon, icon: "check" }),
2458
+ React.createElement(Text, { ml: 3, fontWeight: "medium" }, response.code)))))))));
2459
+ const tabResponses = (React.createElement(TabList, { density: "compact" }, responses.map(({ code }) => (React.createElement(Tab, { key: code, id: code, intent: codeToIntentVal(code) }, code)))));
2422
2460
  return (React.createElement(VStack, { spacing: 8, as: Tabs, selectedId: activeResponseId, onChange: setActiveResponseId, appearance: "pill" },
2423
- React.createElement(SectionTitle, { title: "Responses" },
2424
- React.createElement(TabList, { density: "compact" }, responses.map(({ code }) => (React.createElement(Tab, { key: code, id: code, intent: codeToIntentVal(code) }, code))))),
2425
- React.createElement(TabPanels, { p: 0 }, responses.map(response => (React.createElement(TabPanel, { key: response.code, id: response.code },
2426
- React.createElement(Response, { response: response, onMediaTypeChange: onMediaTypeChange })))))));
2461
+ React.createElement(SectionTitle, { title: "Responses" }, isCompact ? compactResponses : tabResponses),
2462
+ isCompact ? (React.createElement(Response, { response: response, onMediaTypeChange: onMediaTypeChange })) : (React.createElement(TabPanels, { p: 0 }, responses.map(response => (React.createElement(TabPanel, { key: response.code, id: response.code },
2463
+ React.createElement(Response, { response: response, onMediaTypeChange: onMediaTypeChange }))))))));
2427
2464
  };
2428
2465
  Responses.displayName = 'HttpOperation.Responses';
2429
2466
  const Response = ({ response, onMediaTypeChange }) => {
@@ -2468,6 +2505,7 @@ const codeToIntentVal = (code) => {
2468
2505
  const HttpOperationComponent = React.memo(({ className, data: unresolvedData, layoutOptions, tryItCredentialsPolicy, tryItCorsProxy }) => {
2469
2506
  const { nodeHasChanged } = useOptionsCtx();
2470
2507
  const data = useResolvedObject(unresolvedData);
2508
+ const { ref: layoutRef, isCompact } = useIsCompact(layoutOptions);
2471
2509
  const mocking = React.useContext(MockingContext);
2472
2510
  const isDeprecated = !!data.deprecated;
2473
2511
  const isInternal = !!data.internal;
@@ -2477,15 +2515,16 @@ const HttpOperationComponent = React.memo(({ className, data: unresolvedData, la
2477
2515
  const prettyName = (data.summary || data.iid || '').trim();
2478
2516
  const hasBadges = isDeprecated || isInternal;
2479
2517
  const header = (React.createElement(OperationHeader, { id: data.id, method: data.method, path: data.path, noHeading: layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading, hasBadges: hasBadges, name: prettyName, isDeprecated: isDeprecated, isInternal: isInternal }));
2518
+ const tryItPanel = !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryItPanel) && (React.createElement(TryItWithRequestSamples, { httpOperation: data, responseMediaType: responseMediaType, responseStatusCode: responseStatusCode, requestBodyIndex: requestBodyIndex, hideTryIt: layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryIt, tryItCredentialsPolicy: tryItCredentialsPolicy, mockUrl: mocking.hideMocking ? undefined : mocking.mockUrl, corsProxy: tryItCorsProxy }));
2480
2519
  const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: data.id, attr: 'description' });
2481
2520
  const description = (React.createElement(VStack, { spacing: 10 },
2482
2521
  data.description && (React.createElement(Box, { pos: "relative" },
2483
2522
  React.createElement(MarkdownViewer, { className: "HttpOperation__Description", markdown: data.description }),
2484
2523
  React.createElement(NodeAnnotation, { change: descriptionChanged }))),
2485
2524
  React.createElement(Request, { onChange: setTextRequestBodyIndex, operation: data }),
2486
- data.responses && (React.createElement(Responses, { responses: data.responses, onMediaTypeChange: setResponseMediaType, onStatusCodeChange: setResponseStatusCode }))));
2487
- const tryItPanel = !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryItPanel) && (React.createElement(TryItWithRequestSamples, { httpOperation: data, responseMediaType: responseMediaType, responseStatusCode: responseStatusCode, requestBodyIndex: requestBodyIndex, hideTryIt: layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryIt, tryItCredentialsPolicy: tryItCredentialsPolicy, mockUrl: mocking.hideMocking ? undefined : mocking.mockUrl, corsProxy: tryItCorsProxy }));
2488
- return (React.createElement(TwoColumnLayout, { className: cn('HttpOperation', className), header: header, left: description, right: tryItPanel }));
2525
+ data.responses && (React.createElement(Responses, { responses: data.responses, onMediaTypeChange: setResponseMediaType, onStatusCodeChange: setResponseStatusCode, isCompact: isCompact })),
2526
+ isCompact && tryItPanel));
2527
+ return (React.createElement(TwoColumnLayout, { ref: layoutRef, className: cn('HttpOperation', className), header: header, left: description, right: !isCompact && tryItPanel }));
2489
2528
  });
2490
2529
  HttpOperationComponent.displayName = 'HttpOperation.Component';
2491
2530
  const HttpOperation = withErrorBoundary(HttpOperationComponent, {
@@ -2717,22 +2756,6 @@ const HttpServiceComponent = React.memo(({ data: unresolvedData, location = {},
2717
2756
  HttpServiceComponent.displayName = 'HttpService.Component';
2718
2757
  const HttpService = withErrorBoundary(HttpServiceComponent, { recoverableProps: ['data'] });
2719
2758
 
2720
- const getBreakpoints = (compact) => {
2721
- if (!compact)
2722
- return undefined;
2723
- if (typeof compact === 'number') {
2724
- return [
2725
- ['compact', compact],
2726
- ['regular', Infinity],
2727
- ];
2728
- }
2729
- return [['compact', Infinity]];
2730
- };
2731
- function useIsCompact(layoutOptions) {
2732
- const { ref, breakpoint } = useBreakpoints(getBreakpoints(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.compact));
2733
- return { ref: ref, isCompact: breakpoint === 'compact' };
2734
- }
2735
-
2736
2759
  const ModelComponent = ({ data: unresolvedData, className, nodeTitle, layoutOptions, exportProps, }) => {
2737
2760
  var _a, _b;
2738
2761
  const resolveRef = useSchemaInlineRefResolver();
@@ -2783,9 +2806,11 @@ const ModelExamples = React.memo(({ data, isCollapsible = false }) => {
2783
2806
  const Model = withErrorBoundary(ModelComponent, { recoverableProps: ['data'] });
2784
2807
 
2785
2808
  const Docs = React.memo((_a) => {
2809
+ var _b;
2786
2810
  var { nodeType, nodeData, useNodeForRefResolving = false, refResolver, nodeHasChanged } = _a, commonProps = __rest(_a, ["nodeType", "nodeData", "useNodeForRefResolving", "refResolver", "nodeHasChanged"]);
2787
2811
  const parsedNode = useParsedData(nodeType, nodeData);
2788
2812
  if (!parsedNode) {
2813
+ (_b = commonProps.nodeUnsupported) === null || _b === void 0 ? void 0 : _b.call(commonProps, 'dataEmpty');
2789
2814
  return null;
2790
2815
  }
2791
2816
  let elem = React.createElement(ParsedDocs, Object.assign({ node: parsedNode }, commonProps));
@@ -2795,7 +2820,7 @@ const Docs = React.memo((_a) => {
2795
2820
  return React.createElement(ElementsOptionsProvider, { nodeHasChanged: nodeHasChanged }, elem);
2796
2821
  });
2797
2822
  const ParsedDocs = (_a) => {
2798
- var { node } = _a, commonProps = __rest(_a, ["node"]);
2823
+ var { node, nodeUnsupported } = _a, commonProps = __rest(_a, ["node", "nodeUnsupported"]);
2799
2824
  switch (node.type) {
2800
2825
  case 'article':
2801
2826
  return React.createElement(Article, Object.assign({ data: node.data }, commonProps));
@@ -2806,6 +2831,7 @@ const ParsedDocs = (_a) => {
2806
2831
  case 'model':
2807
2832
  return React.createElement(Model, Object.assign({ data: node.data }, commonProps));
2808
2833
  default:
2834
+ nodeUnsupported === null || nodeUnsupported === void 0 ? void 0 : nodeUnsupported('invalidType');
2809
2835
  return null;
2810
2836
  }
2811
2837
  };
package/index.js CHANGED
@@ -542,6 +542,22 @@ function useChosenServerUrl(chosenServerUrl) {
542
542
  };
543
543
  }
544
544
 
545
+ const getBreakpoints = (compact) => {
546
+ if (!compact)
547
+ return undefined;
548
+ if (typeof compact === 'number') {
549
+ return [
550
+ ['compact', compact],
551
+ ['regular', Infinity],
552
+ ];
553
+ }
554
+ return [['compact', Infinity]];
555
+ };
556
+ function useIsCompact(layoutOptions) {
557
+ const { ref, breakpoint } = mosaic.useBreakpoints(getBreakpoints(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.compact));
558
+ return { ref: ref, isCompact: breakpoint === 'compact' };
559
+ }
560
+
545
561
  const chosenServerAtom = jotai.atom(undefined);
546
562
 
547
563
  function isValidServer(server) {
@@ -2466,20 +2482,41 @@ const SecuritySchemes$1 = ({ schemes }) => {
2466
2482
  React__namespace.createElement(mosaic.NodeAnnotation, { change: nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: scheme.id }) }))))));
2467
2483
  };
2468
2484
 
2469
- const Responses = ({ responses: unsortedResponses, onStatusCodeChange, onMediaTypeChange }) => {
2485
+ const Responses = ({ responses: unsortedResponses, onStatusCodeChange, onMediaTypeChange, isCompact, }) => {
2470
2486
  var _a, _b;
2471
2487
  const responses = sortBy__default["default"](uniqBy__default["default"](unsortedResponses, r => r.code), r => r.code);
2472
2488
  const [activeResponseId, setActiveResponseId] = React__namespace.useState((_b = (_a = responses[0]) === null || _a === void 0 ? void 0 : _a.code) !== null && _b !== void 0 ? _b : '');
2489
+ const { isOpen, open, close } = mosaic.useModalState();
2490
+ const onSelectionChange = React__namespace.useCallback(keys => {
2491
+ const selectedId = keys.values().next().value;
2492
+ const selectedResponse = responses === null || responses === void 0 ? void 0 : responses.find(response => response.id === selectedId);
2493
+ if (selectedResponse) {
2494
+ setActiveResponseId(selectedResponse.code);
2495
+ close();
2496
+ }
2497
+ }, [responses, setActiveResponseId, close]);
2473
2498
  React__namespace.useEffect(() => {
2474
2499
  onStatusCodeChange(activeResponseId);
2475
2500
  }, [activeResponseId]);
2476
2501
  if (!responses.length)
2477
2502
  return null;
2503
+ const response = responses.find(r => r.code === activeResponseId) || responses[0];
2504
+ const compactResponses = (React__namespace.createElement(React__namespace.Fragment, null,
2505
+ React__namespace.createElement(mosaic.Button, { onPress: open, iconRight: React__namespace.createElement(mosaic.Icon, { icon: "chevron-down", color: "var(--color-border-button)" }), style: {
2506
+ color: `var(--color-${codeToIntentVal(activeResponseId)})`,
2507
+ } }, activeResponseId),
2508
+ React__namespace.createElement(mosaic.Modal, { title: "Response Code", isOpen: isOpen, onClose: close, size: "sm", footer: React__namespace.createElement(mosaic.HStack, { justifyContent: "end" },
2509
+ React__namespace.createElement(mosaic.Button, { onPress: close, intent: "default", appearance: "primary" }, "Close")) },
2510
+ React__namespace.createElement(mosaic.ListBox, { "aria-label": "Response Code", overflowY: "auto", m: -5, items: responses, selectionMode: "single", onSelectionChange: onSelectionChange }, (response) => (React__namespace.createElement(mosaic.ListBoxItem, { key: response.id },
2511
+ React__namespace.createElement(mosaic.Box, { p: 3, bg: { hover: 'primary-tint' } },
2512
+ React__namespace.createElement(mosaic.Flex, { w: "2xl", align: "center", justify: "end" },
2513
+ response.code === activeResponseId && React__namespace.createElement(mosaic.Box, { as: mosaic.Icon, icon: "check" }),
2514
+ React__namespace.createElement(mosaic.Text, { ml: 3, fontWeight: "medium" }, response.code)))))))));
2515
+ const tabResponses = (React__namespace.createElement(mosaic.TabList, { density: "compact" }, responses.map(({ code }) => (React__namespace.createElement(mosaic.Tab, { key: code, id: code, intent: codeToIntentVal(code) }, code)))));
2478
2516
  return (React__namespace.createElement(mosaic.VStack, { spacing: 8, as: mosaic.Tabs, selectedId: activeResponseId, onChange: setActiveResponseId, appearance: "pill" },
2479
- React__namespace.createElement(SectionTitle, { title: "Responses" },
2480
- React__namespace.createElement(mosaic.TabList, { density: "compact" }, responses.map(({ code }) => (React__namespace.createElement(mosaic.Tab, { key: code, id: code, intent: codeToIntentVal(code) }, code))))),
2481
- React__namespace.createElement(mosaic.TabPanels, { p: 0 }, responses.map(response => (React__namespace.createElement(mosaic.TabPanel, { key: response.code, id: response.code },
2482
- React__namespace.createElement(Response, { response: response, onMediaTypeChange: onMediaTypeChange })))))));
2517
+ React__namespace.createElement(SectionTitle, { title: "Responses" }, isCompact ? compactResponses : tabResponses),
2518
+ isCompact ? (React__namespace.createElement(Response, { response: response, onMediaTypeChange: onMediaTypeChange })) : (React__namespace.createElement(mosaic.TabPanels, { p: 0 }, responses.map(response => (React__namespace.createElement(mosaic.TabPanel, { key: response.code, id: response.code },
2519
+ React__namespace.createElement(Response, { response: response, onMediaTypeChange: onMediaTypeChange }))))))));
2483
2520
  };
2484
2521
  Responses.displayName = 'HttpOperation.Responses';
2485
2522
  const Response = ({ response, onMediaTypeChange }) => {
@@ -2524,6 +2561,7 @@ const codeToIntentVal = (code) => {
2524
2561
  const HttpOperationComponent = React__namespace.memo(({ className, data: unresolvedData, layoutOptions, tryItCredentialsPolicy, tryItCorsProxy }) => {
2525
2562
  const { nodeHasChanged } = useOptionsCtx();
2526
2563
  const data = useResolvedObject(unresolvedData);
2564
+ const { ref: layoutRef, isCompact } = useIsCompact(layoutOptions);
2527
2565
  const mocking = React__namespace.useContext(MockingContext);
2528
2566
  const isDeprecated = !!data.deprecated;
2529
2567
  const isInternal = !!data.internal;
@@ -2533,15 +2571,16 @@ const HttpOperationComponent = React__namespace.memo(({ className, data: unresol
2533
2571
  const prettyName = (data.summary || data.iid || '').trim();
2534
2572
  const hasBadges = isDeprecated || isInternal;
2535
2573
  const header = (React__namespace.createElement(OperationHeader, { id: data.id, method: data.method, path: data.path, noHeading: layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading, hasBadges: hasBadges, name: prettyName, isDeprecated: isDeprecated, isInternal: isInternal }));
2574
+ const tryItPanel = !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryItPanel) && (React__namespace.createElement(TryItWithRequestSamples, { httpOperation: data, responseMediaType: responseMediaType, responseStatusCode: responseStatusCode, requestBodyIndex: requestBodyIndex, hideTryIt: layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryIt, tryItCredentialsPolicy: tryItCredentialsPolicy, mockUrl: mocking.hideMocking ? undefined : mocking.mockUrl, corsProxy: tryItCorsProxy }));
2536
2575
  const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: data.id, attr: 'description' });
2537
2576
  const description = (React__namespace.createElement(mosaic.VStack, { spacing: 10 },
2538
2577
  data.description && (React__namespace.createElement(mosaic.Box, { pos: "relative" },
2539
2578
  React__namespace.createElement(MarkdownViewer, { className: "HttpOperation__Description", markdown: data.description }),
2540
2579
  React__namespace.createElement(mosaic.NodeAnnotation, { change: descriptionChanged }))),
2541
2580
  React__namespace.createElement(Request, { onChange: setTextRequestBodyIndex, operation: data }),
2542
- data.responses && (React__namespace.createElement(Responses, { responses: data.responses, onMediaTypeChange: setResponseMediaType, onStatusCodeChange: setResponseStatusCode }))));
2543
- const tryItPanel = !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryItPanel) && (React__namespace.createElement(TryItWithRequestSamples, { httpOperation: data, responseMediaType: responseMediaType, responseStatusCode: responseStatusCode, requestBodyIndex: requestBodyIndex, hideTryIt: layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryIt, tryItCredentialsPolicy: tryItCredentialsPolicy, mockUrl: mocking.hideMocking ? undefined : mocking.mockUrl, corsProxy: tryItCorsProxy }));
2544
- return (React__namespace.createElement(TwoColumnLayout, { className: cn__default["default"]('HttpOperation', className), header: header, left: description, right: tryItPanel }));
2581
+ data.responses && (React__namespace.createElement(Responses, { responses: data.responses, onMediaTypeChange: setResponseMediaType, onStatusCodeChange: setResponseStatusCode, isCompact: isCompact })),
2582
+ isCompact && tryItPanel));
2583
+ return (React__namespace.createElement(TwoColumnLayout, { ref: layoutRef, className: cn__default["default"]('HttpOperation', className), header: header, left: description, right: !isCompact && tryItPanel }));
2545
2584
  });
2546
2585
  HttpOperationComponent.displayName = 'HttpOperation.Component';
2547
2586
  const HttpOperation = reactErrorBoundary.withErrorBoundary(HttpOperationComponent, {
@@ -2773,22 +2812,6 @@ const HttpServiceComponent = React__namespace.memo(({ data: unresolvedData, loca
2773
2812
  HttpServiceComponent.displayName = 'HttpService.Component';
2774
2813
  const HttpService = reactErrorBoundary.withErrorBoundary(HttpServiceComponent, { recoverableProps: ['data'] });
2775
2814
 
2776
- const getBreakpoints = (compact) => {
2777
- if (!compact)
2778
- return undefined;
2779
- if (typeof compact === 'number') {
2780
- return [
2781
- ['compact', compact],
2782
- ['regular', Infinity],
2783
- ];
2784
- }
2785
- return [['compact', Infinity]];
2786
- };
2787
- function useIsCompact(layoutOptions) {
2788
- const { ref, breakpoint } = mosaic.useBreakpoints(getBreakpoints(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.compact));
2789
- return { ref: ref, isCompact: breakpoint === 'compact' };
2790
- }
2791
-
2792
2815
  const ModelComponent = ({ data: unresolvedData, className, nodeTitle, layoutOptions, exportProps, }) => {
2793
2816
  var _a, _b;
2794
2817
  const resolveRef = useSchemaInlineRefResolver();
@@ -2839,9 +2862,11 @@ const ModelExamples = React__namespace.memo(({ data, isCollapsible = false }) =>
2839
2862
  const Model = reactErrorBoundary.withErrorBoundary(ModelComponent, { recoverableProps: ['data'] });
2840
2863
 
2841
2864
  const Docs = React__namespace.memo((_a) => {
2865
+ var _b;
2842
2866
  var { nodeType, nodeData, useNodeForRefResolving = false, refResolver, nodeHasChanged } = _a, commonProps = tslib.__rest(_a, ["nodeType", "nodeData", "useNodeForRefResolving", "refResolver", "nodeHasChanged"]);
2843
2867
  const parsedNode = useParsedData(nodeType, nodeData);
2844
2868
  if (!parsedNode) {
2869
+ (_b = commonProps.nodeUnsupported) === null || _b === void 0 ? void 0 : _b.call(commonProps, 'dataEmpty');
2845
2870
  return null;
2846
2871
  }
2847
2872
  let elem = React__namespace.createElement(ParsedDocs, Object.assign({ node: parsedNode }, commonProps));
@@ -2851,7 +2876,7 @@ const Docs = React__namespace.memo((_a) => {
2851
2876
  return React__namespace.createElement(ElementsOptionsProvider, { nodeHasChanged: nodeHasChanged }, elem);
2852
2877
  });
2853
2878
  const ParsedDocs = (_a) => {
2854
- var { node } = _a, commonProps = tslib.__rest(_a, ["node"]);
2879
+ var { node, nodeUnsupported } = _a, commonProps = tslib.__rest(_a, ["node", "nodeUnsupported"]);
2855
2880
  switch (node.type) {
2856
2881
  case 'article':
2857
2882
  return React__namespace.createElement(Article, Object.assign({ data: node.data }, commonProps));
@@ -2862,6 +2887,7 @@ const ParsedDocs = (_a) => {
2862
2887
  case 'model':
2863
2888
  return React__namespace.createElement(Model, Object.assign({ data: node.data }, commonProps));
2864
2889
  default:
2890
+ nodeUnsupported === null || nodeUnsupported === void 0 ? void 0 : nodeUnsupported('invalidType');
2865
2891
  return null;
2866
2892
  }
2867
2893
  };
package/index.mjs CHANGED
@@ -7,7 +7,7 @@ import isArray from 'lodash/isArray.js';
7
7
  import isPlainObject from 'lodash/isPlainObject.js';
8
8
  import { NodeType, HttpParamStyles } from '@stoplight/types';
9
9
  import { parse } from '@stoplight/yaml';
10
- import { isArray as isArray$1, Box, Panel, CopyButton, Menu, Button, Text, Flex, Input, Icon, Select, FieldButton, Image, Link, useThemeIsDark, HStack, VStack, InvertTheme, Tooltip, Badge, LinkHeading as LinkHeading$1, NodeAnnotation, Tabs, TabList, Tab, TabPanels, TabPanel, Heading, useClipboard, useBreakpoints, useMosaicContext, Provider as Provider$1 } from '@stoplight/mosaic';
10
+ import { isArray as isArray$1, Box, useBreakpoints, Panel, CopyButton, Menu, Button, Text, Flex, Input, Icon, Select, FieldButton, Image, Link, useThemeIsDark, HStack, VStack, InvertTheme, Tooltip, Badge, LinkHeading as LinkHeading$1, NodeAnnotation, useModalState, Modal, ListBox, ListBoxItem, TabList, Tab, Tabs, TabPanels, TabPanel, Heading, useClipboard, useMosaicContext, Provider as Provider$1 } from '@stoplight/mosaic';
11
11
  import isObject from 'lodash/isObject.js';
12
12
  import { withErrorBoundary } from '@stoplight/react-error-boundary';
13
13
  import { useLocation, BrowserRouter, MemoryRouter, HashRouter, StaticRouter, Route } from 'react-router-dom';
@@ -486,6 +486,22 @@ function useChosenServerUrl(chosenServerUrl) {
486
486
  };
487
487
  }
488
488
 
489
+ const getBreakpoints = (compact) => {
490
+ if (!compact)
491
+ return undefined;
492
+ if (typeof compact === 'number') {
493
+ return [
494
+ ['compact', compact],
495
+ ['regular', Infinity],
496
+ ];
497
+ }
498
+ return [['compact', Infinity]];
499
+ };
500
+ function useIsCompact(layoutOptions) {
501
+ const { ref, breakpoint } = useBreakpoints(getBreakpoints(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.compact));
502
+ return { ref: ref, isCompact: breakpoint === 'compact' };
503
+ }
504
+
489
505
  const chosenServerAtom = atom(undefined);
490
506
 
491
507
  function isValidServer(server) {
@@ -2410,20 +2426,41 @@ const SecuritySchemes$1 = ({ schemes }) => {
2410
2426
  React.createElement(NodeAnnotation, { change: nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: scheme.id }) }))))));
2411
2427
  };
2412
2428
 
2413
- const Responses = ({ responses: unsortedResponses, onStatusCodeChange, onMediaTypeChange }) => {
2429
+ const Responses = ({ responses: unsortedResponses, onStatusCodeChange, onMediaTypeChange, isCompact, }) => {
2414
2430
  var _a, _b;
2415
2431
  const responses = sortBy(uniqBy(unsortedResponses, r => r.code), r => r.code);
2416
2432
  const [activeResponseId, setActiveResponseId] = React.useState((_b = (_a = responses[0]) === null || _a === void 0 ? void 0 : _a.code) !== null && _b !== void 0 ? _b : '');
2433
+ const { isOpen, open, close } = useModalState();
2434
+ const onSelectionChange = React.useCallback(keys => {
2435
+ const selectedId = keys.values().next().value;
2436
+ const selectedResponse = responses === null || responses === void 0 ? void 0 : responses.find(response => response.id === selectedId);
2437
+ if (selectedResponse) {
2438
+ setActiveResponseId(selectedResponse.code);
2439
+ close();
2440
+ }
2441
+ }, [responses, setActiveResponseId, close]);
2417
2442
  React.useEffect(() => {
2418
2443
  onStatusCodeChange(activeResponseId);
2419
2444
  }, [activeResponseId]);
2420
2445
  if (!responses.length)
2421
2446
  return null;
2447
+ const response = responses.find(r => r.code === activeResponseId) || responses[0];
2448
+ const compactResponses = (React.createElement(React.Fragment, null,
2449
+ React.createElement(Button, { onPress: open, iconRight: React.createElement(Icon, { icon: "chevron-down", color: "var(--color-border-button)" }), style: {
2450
+ color: `var(--color-${codeToIntentVal(activeResponseId)})`,
2451
+ } }, activeResponseId),
2452
+ React.createElement(Modal, { title: "Response Code", isOpen: isOpen, onClose: close, size: "sm", footer: React.createElement(HStack, { justifyContent: "end" },
2453
+ React.createElement(Button, { onPress: close, intent: "default", appearance: "primary" }, "Close")) },
2454
+ React.createElement(ListBox, { "aria-label": "Response Code", overflowY: "auto", m: -5, items: responses, selectionMode: "single", onSelectionChange: onSelectionChange }, (response) => (React.createElement(ListBoxItem, { key: response.id },
2455
+ React.createElement(Box, { p: 3, bg: { hover: 'primary-tint' } },
2456
+ React.createElement(Flex, { w: "2xl", align: "center", justify: "end" },
2457
+ response.code === activeResponseId && React.createElement(Box, { as: Icon, icon: "check" }),
2458
+ React.createElement(Text, { ml: 3, fontWeight: "medium" }, response.code)))))))));
2459
+ const tabResponses = (React.createElement(TabList, { density: "compact" }, responses.map(({ code }) => (React.createElement(Tab, { key: code, id: code, intent: codeToIntentVal(code) }, code)))));
2422
2460
  return (React.createElement(VStack, { spacing: 8, as: Tabs, selectedId: activeResponseId, onChange: setActiveResponseId, appearance: "pill" },
2423
- React.createElement(SectionTitle, { title: "Responses" },
2424
- React.createElement(TabList, { density: "compact" }, responses.map(({ code }) => (React.createElement(Tab, { key: code, id: code, intent: codeToIntentVal(code) }, code))))),
2425
- React.createElement(TabPanels, { p: 0 }, responses.map(response => (React.createElement(TabPanel, { key: response.code, id: response.code },
2426
- React.createElement(Response, { response: response, onMediaTypeChange: onMediaTypeChange })))))));
2461
+ React.createElement(SectionTitle, { title: "Responses" }, isCompact ? compactResponses : tabResponses),
2462
+ isCompact ? (React.createElement(Response, { response: response, onMediaTypeChange: onMediaTypeChange })) : (React.createElement(TabPanels, { p: 0 }, responses.map(response => (React.createElement(TabPanel, { key: response.code, id: response.code },
2463
+ React.createElement(Response, { response: response, onMediaTypeChange: onMediaTypeChange }))))))));
2427
2464
  };
2428
2465
  Responses.displayName = 'HttpOperation.Responses';
2429
2466
  const Response = ({ response, onMediaTypeChange }) => {
@@ -2468,6 +2505,7 @@ const codeToIntentVal = (code) => {
2468
2505
  const HttpOperationComponent = React.memo(({ className, data: unresolvedData, layoutOptions, tryItCredentialsPolicy, tryItCorsProxy }) => {
2469
2506
  const { nodeHasChanged } = useOptionsCtx();
2470
2507
  const data = useResolvedObject(unresolvedData);
2508
+ const { ref: layoutRef, isCompact } = useIsCompact(layoutOptions);
2471
2509
  const mocking = React.useContext(MockingContext);
2472
2510
  const isDeprecated = !!data.deprecated;
2473
2511
  const isInternal = !!data.internal;
@@ -2477,15 +2515,16 @@ const HttpOperationComponent = React.memo(({ className, data: unresolvedData, la
2477
2515
  const prettyName = (data.summary || data.iid || '').trim();
2478
2516
  const hasBadges = isDeprecated || isInternal;
2479
2517
  const header = (React.createElement(OperationHeader, { id: data.id, method: data.method, path: data.path, noHeading: layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading, hasBadges: hasBadges, name: prettyName, isDeprecated: isDeprecated, isInternal: isInternal }));
2518
+ const tryItPanel = !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryItPanel) && (React.createElement(TryItWithRequestSamples, { httpOperation: data, responseMediaType: responseMediaType, responseStatusCode: responseStatusCode, requestBodyIndex: requestBodyIndex, hideTryIt: layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryIt, tryItCredentialsPolicy: tryItCredentialsPolicy, mockUrl: mocking.hideMocking ? undefined : mocking.mockUrl, corsProxy: tryItCorsProxy }));
2480
2519
  const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: data.id, attr: 'description' });
2481
2520
  const description = (React.createElement(VStack, { spacing: 10 },
2482
2521
  data.description && (React.createElement(Box, { pos: "relative" },
2483
2522
  React.createElement(MarkdownViewer, { className: "HttpOperation__Description", markdown: data.description }),
2484
2523
  React.createElement(NodeAnnotation, { change: descriptionChanged }))),
2485
2524
  React.createElement(Request, { onChange: setTextRequestBodyIndex, operation: data }),
2486
- data.responses && (React.createElement(Responses, { responses: data.responses, onMediaTypeChange: setResponseMediaType, onStatusCodeChange: setResponseStatusCode }))));
2487
- const tryItPanel = !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryItPanel) && (React.createElement(TryItWithRequestSamples, { httpOperation: data, responseMediaType: responseMediaType, responseStatusCode: responseStatusCode, requestBodyIndex: requestBodyIndex, hideTryIt: layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryIt, tryItCredentialsPolicy: tryItCredentialsPolicy, mockUrl: mocking.hideMocking ? undefined : mocking.mockUrl, corsProxy: tryItCorsProxy }));
2488
- return (React.createElement(TwoColumnLayout, { className: cn('HttpOperation', className), header: header, left: description, right: tryItPanel }));
2525
+ data.responses && (React.createElement(Responses, { responses: data.responses, onMediaTypeChange: setResponseMediaType, onStatusCodeChange: setResponseStatusCode, isCompact: isCompact })),
2526
+ isCompact && tryItPanel));
2527
+ return (React.createElement(TwoColumnLayout, { ref: layoutRef, className: cn('HttpOperation', className), header: header, left: description, right: !isCompact && tryItPanel }));
2489
2528
  });
2490
2529
  HttpOperationComponent.displayName = 'HttpOperation.Component';
2491
2530
  const HttpOperation = withErrorBoundary(HttpOperationComponent, {
@@ -2717,22 +2756,6 @@ const HttpServiceComponent = React.memo(({ data: unresolvedData, location = {},
2717
2756
  HttpServiceComponent.displayName = 'HttpService.Component';
2718
2757
  const HttpService = withErrorBoundary(HttpServiceComponent, { recoverableProps: ['data'] });
2719
2758
 
2720
- const getBreakpoints = (compact) => {
2721
- if (!compact)
2722
- return undefined;
2723
- if (typeof compact === 'number') {
2724
- return [
2725
- ['compact', compact],
2726
- ['regular', Infinity],
2727
- ];
2728
- }
2729
- return [['compact', Infinity]];
2730
- };
2731
- function useIsCompact(layoutOptions) {
2732
- const { ref, breakpoint } = useBreakpoints(getBreakpoints(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.compact));
2733
- return { ref: ref, isCompact: breakpoint === 'compact' };
2734
- }
2735
-
2736
2759
  const ModelComponent = ({ data: unresolvedData, className, nodeTitle, layoutOptions, exportProps, }) => {
2737
2760
  var _a, _b;
2738
2761
  const resolveRef = useSchemaInlineRefResolver();
@@ -2783,9 +2806,11 @@ const ModelExamples = React.memo(({ data, isCollapsible = false }) => {
2783
2806
  const Model = withErrorBoundary(ModelComponent, { recoverableProps: ['data'] });
2784
2807
 
2785
2808
  const Docs = React.memo((_a) => {
2809
+ var _b;
2786
2810
  var { nodeType, nodeData, useNodeForRefResolving = false, refResolver, nodeHasChanged } = _a, commonProps = __rest(_a, ["nodeType", "nodeData", "useNodeForRefResolving", "refResolver", "nodeHasChanged"]);
2787
2811
  const parsedNode = useParsedData(nodeType, nodeData);
2788
2812
  if (!parsedNode) {
2813
+ (_b = commonProps.nodeUnsupported) === null || _b === void 0 ? void 0 : _b.call(commonProps, 'dataEmpty');
2789
2814
  return null;
2790
2815
  }
2791
2816
  let elem = React.createElement(ParsedDocs, Object.assign({ node: parsedNode }, commonProps));
@@ -2795,7 +2820,7 @@ const Docs = React.memo((_a) => {
2795
2820
  return React.createElement(ElementsOptionsProvider, { nodeHasChanged: nodeHasChanged }, elem);
2796
2821
  });
2797
2822
  const ParsedDocs = (_a) => {
2798
- var { node } = _a, commonProps = __rest(_a, ["node"]);
2823
+ var { node, nodeUnsupported } = _a, commonProps = __rest(_a, ["node", "nodeUnsupported"]);
2799
2824
  switch (node.type) {
2800
2825
  case 'article':
2801
2826
  return React.createElement(Article, Object.assign({ data: node.data }, commonProps));
@@ -2806,6 +2831,7 @@ const ParsedDocs = (_a) => {
2806
2831
  case 'model':
2807
2832
  return React.createElement(Model, Object.assign({ data: node.data }, commonProps));
2808
2833
  default:
2834
+ nodeUnsupported === null || nodeUnsupported === void 0 ? void 0 : nodeUnsupported('invalidType');
2809
2835
  return null;
2810
2836
  }
2811
2837
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stoplight/elements-core",
3
- "version": "7.9.0",
3
+ "version": "7.11.0",
4
4
  "main": "./index.js",
5
5
  "sideEffects": [
6
6
  "web-components.min.js",