@stoplight/elements-core 9.0.12 → 9.0.13-alpha-0.1

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.
@@ -21,6 +21,7 @@ export declare type TableOfContentsGroup = {
21
21
  title: string;
22
22
  items: TableOfContentsGroupItem[];
23
23
  itemsType?: 'article' | 'http_operation' | 'http_webhook' | 'model';
24
+ index: string;
24
25
  };
25
26
  export declare type TableOfContentsExternalLink = {
26
27
  title: string;
@@ -33,5 +34,11 @@ export declare type TableOfContentsNode<T = 'http_service' | 'http_operation' |
33
34
  type: T;
34
35
  meta: string;
35
36
  version?: string;
37
+ index: string;
36
38
  };
37
39
  export declare type TableOfContentsNodeGroup = TableOfContentsNode<'http_service'> & TableOfContentsGroup;
40
+ export declare type ActiveItemContextType = {
41
+ activeId: string | undefined;
42
+ lastActiveIndex: string;
43
+ setLastActiveIndex: React.Dispatch<React.SetStateAction<string>>;
44
+ };
@@ -4,12 +4,12 @@ import { JSONSchema7, JSONSchema7Definition } from 'json-schema';
4
4
  export declare type ParameterSpec = Pick<IHttpParam, 'name' | 'schema' | 'required'> & {
5
5
  examples?: (Omit<INodeExample, 'id'> | Omit<INodeExternalExample, 'id'>)[];
6
6
  };
7
- export declare function parameterOptions(parameter: ParameterSpec): ({
7
+ export declare function encodeSafeSelectorValue(value: string | number): string | number;
8
+ export declare function decodeSafeSelectorValue(value: string | number): string | number;
9
+ export declare function parameterOptions(parameter: ParameterSpec): {
8
10
  value: string | number;
9
- } | {
10
11
  label: string;
11
- value: string;
12
- })[] | null;
12
+ }[] | null;
13
13
  export declare const selectExampleOption: {
14
14
  value: string;
15
15
  label: string;
package/index.esm.js CHANGED
@@ -1340,8 +1340,50 @@ const booleanOptions = [
1340
1340
  { label: 'False', value: 'false' },
1341
1341
  { label: 'True', value: 'true' },
1342
1342
  ];
1343
+ function encodeSafeSelectorValue(value) {
1344
+ if (typeof value === 'number') {
1345
+ return value;
1346
+ }
1347
+ const hasSpecialChars = /["'\[\]\\(){}]/.test(value);
1348
+ if (!hasSpecialChars) {
1349
+ return value;
1350
+ }
1351
+ try {
1352
+ return 'b64:' + btoa(value);
1353
+ }
1354
+ catch (e) {
1355
+ return 'enc:' + encodeURIComponent(value);
1356
+ }
1357
+ }
1358
+ function decodeSafeSelectorValue(value) {
1359
+ if (typeof value === 'number') {
1360
+ return value;
1361
+ }
1362
+ if (value.startsWith('b64:')) {
1363
+ try {
1364
+ return atob(value.substring(4));
1365
+ }
1366
+ catch (e) {
1367
+ return value;
1368
+ }
1369
+ }
1370
+ if (value.startsWith('enc:')) {
1371
+ try {
1372
+ return decodeURIComponent(value.substring(4));
1373
+ }
1374
+ catch (e) {
1375
+ return value;
1376
+ }
1377
+ }
1378
+ return value;
1379
+ }
1343
1380
  function enumOptions(enumValues, required) {
1344
- const options = map(enumValues, v => ({ value: typeof v === 'number' ? v : String(v) }));
1381
+ const options = map(enumValues, v => {
1382
+ var _a;
1383
+ const stringValue = typeof v === 'object' && v !== null ? (_a = safeStringify(v)) !== null && _a !== void 0 ? _a : String(v) : typeof v === 'number' ? v : String(v);
1384
+ const safeValue = encodeSafeSelectorValue(stringValue);
1385
+ return { value: safeValue, label: String(stringValue) };
1386
+ });
1345
1387
  return required ? options : [{ label: 'Not Set', value: '' }, ...options];
1346
1388
  }
1347
1389
  function parameterOptions(parameter) {
@@ -1371,15 +1413,16 @@ function parameterSupportsFileUpload(parameter) {
1371
1413
  ((_c = parameter.schema) === null || _c === void 0 ? void 0 : _c.contentMediaType) === 'application/octet-stream'));
1372
1414
  }
1373
1415
  function stringifyValue(value) {
1374
- return typeof value === 'object' ? JSON.stringify(value) : escapeQuotes(String(value));
1416
+ var _a;
1417
+ if (typeof value === 'object' && value !== null) {
1418
+ return (_a = safeStringify(value)) !== null && _a !== void 0 ? _a : String(value);
1419
+ }
1420
+ return String(value);
1375
1421
  }
1376
1422
  function exampleValue(example) {
1377
1423
  const value = 'value' in example ? example.value : example.externalValue;
1378
1424
  return stringifyValue(value);
1379
1425
  }
1380
- function escapeQuotes(value) {
1381
- return value.replace(/"/g, '\\"');
1382
- }
1383
1426
  function getPlaceholderForParameter(parameter) {
1384
1427
  var _a, _b;
1385
1428
  const { value: parameterValue, isDefault } = getValueForParameter(parameter);
@@ -1405,14 +1448,14 @@ const getValueForParameter = (parameter) => {
1405
1448
  if (typeof defaultValue !== 'undefined') {
1406
1449
  return { value: stringifyValue(defaultValue), isDefault: true };
1407
1450
  }
1408
- const examples = (_a = parameter.examples) !== null && _a !== void 0 ? _a : [];
1409
- if (examples.length > 0) {
1410
- return { value: exampleValue(examples[0]) };
1411
- }
1412
- const enums = (_c = (_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.enum) !== null && _c !== void 0 ? _c : [];
1451
+ const enums = (_b = (_a = parameter.schema) === null || _a === void 0 ? void 0 : _a.enum) !== null && _b !== void 0 ? _b : [];
1413
1452
  if (enums.length > 0) {
1414
1453
  return { value: stringifyValue(enums[0]) };
1415
1454
  }
1455
+ const examples = (_c = parameter.examples) !== null && _c !== void 0 ? _c : [];
1456
+ if (examples.length > 0) {
1457
+ return { value: exampleValue(examples[0]) };
1458
+ }
1416
1459
  return { value: '' };
1417
1460
  };
1418
1461
  const getInitialValueForParameter = (parameter) => {
@@ -1468,11 +1511,19 @@ const ParameterEditor = ({ parameter, value, onChange, isOptional, onChangeOptio
1468
1511
  const examples = exampleOptions(parameter);
1469
1512
  const selectedExample = (_a = examples === null || examples === void 0 ? void 0 : examples.find(e => e.value === value)) !== null && _a !== void 0 ? _a : selectExampleOption;
1470
1513
  const parameterDisplayName = `${parameter.name}${parameter.required ? '*' : ''}`;
1514
+ const encodedValue = React.useMemo(() => {
1515
+ if (!value || !parameterValueOptions)
1516
+ return value || '';
1517
+ const matchingOption = parameterValueOptions.find(opt => {
1518
+ return String(decodeSafeSelectorValue(opt.value)) === value;
1519
+ });
1520
+ return matchingOption ? String(matchingOption.value) : value;
1521
+ }, [value, parameterValueOptions]);
1471
1522
  const requiredButEmpty = validate && parameter.required && !value;
1472
1523
  return (React.createElement(React.Fragment, null,
1473
1524
  React.createElement(Text, { as: "label", "aria-hidden": "true", "data-testid": "param-label", htmlFor: inputId, fontSize: "base" }, parameterDisplayName),
1474
1525
  React.createElement(Text, { mx: 3 }, ":"),
1475
- React.createElement("div", null, parameterValueOptions ? (React.createElement(Select, { flex: 1, "aria-label": parameter.name, options: parameterValueOptions, value: value || '', onChange: onChange, placeholder: getPlaceholderForSelectedParameter(parameter) })) : (React.createElement(Flex, { flex: 1 },
1526
+ React.createElement("div", null, parameterValueOptions ? (React.createElement(Select, { flex: 1, "aria-label": parameter.name, options: parameterValueOptions, value: encodedValue, onChange: val => onChange && onChange(String(decodeSafeSelectorValue(val))), placeholder: getPlaceholderForSelectedParameter(parameter) })) : (React.createElement(Flex, { flex: 1 },
1476
1527
  React.createElement(Input, { id: inputId, "aria-label": parameter.name, appearance: requiredButEmpty ? 'default' : 'minimal', flex: 1, placeholder: getPlaceholderForParameter(parameter), type: ((_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.type) === 'number' ? 'number' : 'text', required: true, intent: requiredButEmpty ? 'danger' : 'default', value: value || '', onChange: e => onChange && onChange(e.currentTarget.value) }),
1477
1528
  examples && (React.createElement(Select, { "aria-label": `${parameter.name}-select`, flex: 1, value: selectedExample.value, options: examples, onChange: onChange }))))),
1478
1529
  canChangeOptional && !parameter.required && (React.createElement(React.Fragment, null,
@@ -2327,10 +2378,17 @@ ServersDropdown.displayName = 'ServersDropdown';
2327
2378
 
2328
2379
  const VariableEditor = ({ variable, value, onChange }) => {
2329
2380
  const inputId = useUniqueId(`id_${variable.name}_`);
2381
+ const encodedOptions = React.useMemo(() => (variable.enum ? variable.enum.map(s => ({ value: encodeSafeSelectorValue(s), label: String(s) })) : []), [variable.enum]);
2382
+ const encodedValue = React.useMemo(() => {
2383
+ if (!value || !variable.enum)
2384
+ return value || variable.default;
2385
+ const matchingOption = encodedOptions.find(opt => decodeSafeSelectorValue(String(opt.value)) === value);
2386
+ return matchingOption ? String(matchingOption.value) : value;
2387
+ }, [value, variable.enum, variable.default, encodedOptions]);
2330
2388
  return (React.createElement(React.Fragment, null,
2331
2389
  React.createElement(Text, { as: "label", "aria-hidden": "true", "data-testid": "param-label", htmlFor: inputId, fontSize: "base" }, variable.name),
2332
2390
  React.createElement(Text, { mx: 3 }, ":"),
2333
- React.createElement("div", null, variable.enum ? (React.createElement(Select, { flex: 1, "aria-label": variable.name, options: variable.enum.map(s => ({ value: s })), value: value || variable.default, onChange: onChange })) : (React.createElement(Flex, { flex: 1 },
2391
+ React.createElement("div", null, variable.enum ? (React.createElement(Select, { flex: 1, "aria-label": variable.name, options: encodedOptions, value: encodedValue, onChange: val => onChange && onChange(decodeSafeSelectorValue(String(val))) })) : (React.createElement(Flex, { flex: 1 },
2334
2392
  React.createElement(Input, { id: inputId, "aria-label": variable.name, appearance: 'minimal', flex: 1, placeholder: variable.default, type: "text", required: true, intent: 'default', value: value || '', onChange: e => onChange && onChange(e.currentTarget.value) }))))));
2335
2393
  };
2336
2394
 
@@ -3496,7 +3554,7 @@ function findFirstNode(items) {
3496
3554
  return;
3497
3555
  }
3498
3556
  function isDivider(item) {
3499
- return Object.keys(item).length === 1 && 'title' in item;
3557
+ return Object.keys(item).length === 2 && 'title' in item && 'index' in item;
3500
3558
  }
3501
3559
  function isGroup(item) {
3502
3560
  return Object.keys(item).length >= 2 && 'title' in item && 'items' in item;
@@ -3508,13 +3566,33 @@ function isNode(item) {
3508
3566
  return 'title' in item && 'slug' in item && 'id' in item && 'meta' in item && 'type' in item;
3509
3567
  }
3510
3568
  function isExternalLink(item) {
3511
- return Object.keys(item).length === 2 && 'title' in item && 'url' in item;
3569
+ return Object.keys(item).length === 3 && 'title' in item && 'url' in item && 'index' in item;
3512
3570
  }
3513
3571
 
3514
- const ActiveIdContext = React.createContext(undefined);
3572
+ const ActiveItemContext = React.createContext({
3573
+ activeId: undefined,
3574
+ lastActiveIndex: '',
3575
+ setLastActiveIndex: () => { },
3576
+ });
3515
3577
  const LinkContext = React.createContext(undefined);
3516
3578
  LinkContext.displayName = 'LinkContext';
3517
3579
  const TableOfContents = React.memo(({ tree, activeId, Link, maxDepthOpenByDefault, externalScrollbar = false, isInResponsiveMode = false, onLinkClick, }) => {
3580
+ const [lastActiveIndex, setLastActiveIndex] = useState('');
3581
+ const value = React.useMemo(() => ({
3582
+ lastActiveIndex,
3583
+ setLastActiveIndex,
3584
+ activeId,
3585
+ }), [lastActiveIndex, activeId]);
3586
+ const updateTocTree = React.useCallback((arr, parentId) => {
3587
+ return arr.map((item, key) => {
3588
+ let newItem = Object.assign(Object.assign({}, item), { index: parentId + key + '-' });
3589
+ if (isGroup(item) || isNodeGroup(item)) {
3590
+ newItem.items = updateTocTree(item.items, parentId + key + '-');
3591
+ }
3592
+ return newItem;
3593
+ });
3594
+ }, []);
3595
+ const updatedTree = updateTocTree(tree, '');
3518
3596
  const container = React.useRef(null);
3519
3597
  const child = React.useRef(null);
3520
3598
  const firstRender = useFirstRender();
@@ -3534,18 +3612,30 @@ const TableOfContents = React.memo(({ tree, activeId, Link, maxDepthOpenByDefaul
3534
3612
  return (React.createElement(Box, { ref: container, w: "full", bg: isInResponsiveMode ? 'canvas' : 'canvas-100', overflowY: "auto" },
3535
3613
  React.createElement(Box, { ref: child, my: 3 },
3536
3614
  React.createElement(LinkContext.Provider, { value: Link },
3537
- React.createElement(ActiveIdContext.Provider, { value: activeId }, tree.map((item, key) => {
3538
- if (isDivider(item)) {
3539
- return React.createElement(Divider, { key: key, item: item, isInResponsiveMode: isInResponsiveMode });
3540
- }
3541
- return (React.createElement(GroupItem, { key: key, item: item, depth: 0, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3542
- }))))));
3615
+ React.createElement(ActiveItemContext.Provider, { value: value },
3616
+ React.createElement(TOCContainer, { updatedTree: updatedTree }, updatedTree.map((item, key) => {
3617
+ if (isDivider(item)) {
3618
+ return React.createElement(Divider, { key: key, item: item, isInResponsiveMode: isInResponsiveMode });
3619
+ }
3620
+ return (React.createElement(GroupItem, { key: key, item: item, depth: 0, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3621
+ })))))));
3543
3622
  });
3544
3623
  TableOfContents.displayName = 'TableOfContents';
3545
3624
  const Divider = React.memo(({ item, isInResponsiveMode = false }) => {
3546
3625
  return (React.createElement(Box, { pl: 4, mb: 2, mt: 6, textTransform: "uppercase", fontSize: isInResponsiveMode ? 'lg' : 'sm', lineHeight: "relaxed", letterSpacing: "wide", fontWeight: "bold" }, item.title));
3547
3626
  });
3548
3627
  Divider.displayName = 'Divider';
3628
+ const TOCContainer = React.memo(({ children, updatedTree }) => {
3629
+ const { setLastActiveIndex } = React.useContext(ActiveItemContext);
3630
+ React.useEffect(() => {
3631
+ const firstNode = findFirstNode(updatedTree);
3632
+ if (firstNode) {
3633
+ setLastActiveIndex(firstNode.index);
3634
+ }
3635
+ }, []);
3636
+ return React.createElement(Box, null, children);
3637
+ });
3638
+ TOCContainer.displayName = 'TOCContainer';
3549
3639
  const GroupItem = React.memo(({ item, depth, maxDepthOpenByDefault, isInResponsiveMode, onLinkClick }) => {
3550
3640
  if (isExternalLink(item)) {
3551
3641
  return (React.createElement(Box, { as: "a", href: item.url, target: "_blank", rel: "noopener noreferrer", display: "block" },
@@ -3563,9 +3653,24 @@ const GroupItem = React.memo(({ item, depth, maxDepthOpenByDefault, isInResponsi
3563
3653
  });
3564
3654
  GroupItem.displayName = 'GroupItem';
3565
3655
  const Group = React.memo(({ depth, item, maxDepthOpenByDefault, isInResponsiveMode, onLinkClick = () => { } }) => {
3566
- const activeId = React.useContext(ActiveIdContext);
3656
+ const { activeId, lastActiveIndex } = React.useContext(ActiveItemContext);
3567
3657
  const [isOpen, setIsOpen] = React.useState(() => isGroupOpenByDefault(depth, item, activeId, maxDepthOpenByDefault));
3568
- const hasActive = !!activeId && hasActiveItem(item.items, activeId);
3658
+ const isActiveGroup = React.useCallback((items, activeId, contextIndex) => {
3659
+ return items.some(element => {
3660
+ const hasSlugOrId = 'slug' in element || 'id' in element;
3661
+ const hasItems = 'items' in element && Array.isArray(element.items);
3662
+ if (!hasSlugOrId && !hasItems)
3663
+ return false;
3664
+ if (activeId &&
3665
+ 'index' in element &&
3666
+ (element.slug === activeId || element.id === activeId) &&
3667
+ element.index === contextIndex) {
3668
+ return true;
3669
+ }
3670
+ return hasItems ? isActiveGroup(element.items, activeId, contextIndex) : false;
3671
+ });
3672
+ }, []);
3673
+ const hasActive = isActiveGroup(item.items, activeId, lastActiveIndex);
3569
3674
  React.useEffect(() => {
3570
3675
  const openByDefault = isGroupOpenByDefault(depth, item, activeId, maxDepthOpenByDefault);
3571
3676
  if (isOpen !== openByDefault) {
@@ -3615,8 +3720,10 @@ const Item = React.memo(({ depth, isActive, id, title, meta, icon, isInResponsiv
3615
3720
  });
3616
3721
  Item.displayName = 'Item';
3617
3722
  const Node = React.memo(({ item, depth, meta, showAsActive, isInResponsiveMode, onClick, onLinkClick = () => { } }) => {
3618
- const activeId = React.useContext(ActiveIdContext);
3619
- const isActive = activeId === item.slug || activeId === item.id;
3723
+ const { activeId, lastActiveIndex, setLastActiveIndex } = React.useContext(ActiveItemContext);
3724
+ const { index } = item;
3725
+ const isSlugMatched = activeId === item.slug || activeId === item.id;
3726
+ const isActive = lastActiveIndex === index && isSlugMatched;
3620
3727
  const LinkComponent = React.useContext(LinkContext);
3621
3728
  const handleClick = (e) => {
3622
3729
  if (isActive) {
@@ -3624,6 +3731,7 @@ const Node = React.memo(({ item, depth, meta, showAsActive, isInResponsiveMode,
3624
3731
  e.preventDefault();
3625
3732
  }
3626
3733
  else {
3734
+ setLastActiveIndex(index);
3627
3735
  onLinkClick();
3628
3736
  }
3629
3737
  if (onClick) {
@@ -3631,7 +3739,7 @@ const Node = React.memo(({ item, depth, meta, showAsActive, isInResponsiveMode,
3631
3739
  }
3632
3740
  };
3633
3741
  return (React.createElement(Box, { as: LinkComponent, to: resolveRelativeLink(item.slug), display: "block", textDecoration: "no-underline", className: "ElementsTableOfContentsItem" },
3634
- React.createElement(Item, { id: getHtmlIdFromItemId(item.slug || item.id), isActive: isActive || showAsActive, depth: depth, title: item.title, icon: NODE_TYPE_TITLE_ICON[item.type] && (React.createElement(Box, { as: Icon, color: NODE_TYPE_ICON_COLOR[item.type], icon: NODE_TYPE_TITLE_ICON[item.type] })), meta: meta, isInResponsiveMode: isInResponsiveMode, onClick: handleClick })));
3742
+ React.createElement(Item, { id: getHtmlIdFromItemId(item.slug || item.id), isActive: isActive || showAsActive, depth: depth, title: item.title, icon: NODE_TYPE_TITLE_ICON[item.type] && (React.createElement(Box, { as: Icon, color: NODE_TYPE_ICON_COLOR[item.type], icon: NODE_TYPE_TITLE_ICON[item.type] })), meta: meta, isInResponsiveMode: isInResponsiveMode, onClick: e => handleClick(e) })));
3635
3743
  });
3636
3744
  Node.displayName = 'Node';
3637
3745
  const Version = ({ value }) => {
package/index.js CHANGED
@@ -1361,8 +1361,50 @@ const booleanOptions = [
1361
1361
  { label: 'False', value: 'false' },
1362
1362
  { label: 'True', value: 'true' },
1363
1363
  ];
1364
+ function encodeSafeSelectorValue(value) {
1365
+ if (typeof value === 'number') {
1366
+ return value;
1367
+ }
1368
+ const hasSpecialChars = /["'\[\]\\(){}]/.test(value);
1369
+ if (!hasSpecialChars) {
1370
+ return value;
1371
+ }
1372
+ try {
1373
+ return 'b64:' + btoa(value);
1374
+ }
1375
+ catch (e) {
1376
+ return 'enc:' + encodeURIComponent(value);
1377
+ }
1378
+ }
1379
+ function decodeSafeSelectorValue(value) {
1380
+ if (typeof value === 'number') {
1381
+ return value;
1382
+ }
1383
+ if (value.startsWith('b64:')) {
1384
+ try {
1385
+ return atob(value.substring(4));
1386
+ }
1387
+ catch (e) {
1388
+ return value;
1389
+ }
1390
+ }
1391
+ if (value.startsWith('enc:')) {
1392
+ try {
1393
+ return decodeURIComponent(value.substring(4));
1394
+ }
1395
+ catch (e) {
1396
+ return value;
1397
+ }
1398
+ }
1399
+ return value;
1400
+ }
1364
1401
  function enumOptions(enumValues, required) {
1365
- const options = map(enumValues, v => ({ value: typeof v === 'number' ? v : String(v) }));
1402
+ const options = map(enumValues, v => {
1403
+ var _a;
1404
+ const stringValue = typeof v === 'object' && v !== null ? (_a = json.safeStringify(v)) !== null && _a !== void 0 ? _a : String(v) : typeof v === 'number' ? v : String(v);
1405
+ const safeValue = encodeSafeSelectorValue(stringValue);
1406
+ return { value: safeValue, label: String(stringValue) };
1407
+ });
1366
1408
  return required ? options : [{ label: 'Not Set', value: '' }, ...options];
1367
1409
  }
1368
1410
  function parameterOptions(parameter) {
@@ -1392,15 +1434,16 @@ function parameterSupportsFileUpload(parameter) {
1392
1434
  ((_c = parameter.schema) === null || _c === void 0 ? void 0 : _c.contentMediaType) === 'application/octet-stream'));
1393
1435
  }
1394
1436
  function stringifyValue(value) {
1395
- return typeof value === 'object' ? JSON.stringify(value) : escapeQuotes(String(value));
1437
+ var _a;
1438
+ if (typeof value === 'object' && value !== null) {
1439
+ return (_a = json.safeStringify(value)) !== null && _a !== void 0 ? _a : String(value);
1440
+ }
1441
+ return String(value);
1396
1442
  }
1397
1443
  function exampleValue(example) {
1398
1444
  const value = 'value' in example ? example.value : example.externalValue;
1399
1445
  return stringifyValue(value);
1400
1446
  }
1401
- function escapeQuotes(value) {
1402
- return value.replace(/"/g, '\\"');
1403
- }
1404
1447
  function getPlaceholderForParameter(parameter) {
1405
1448
  var _a, _b;
1406
1449
  const { value: parameterValue, isDefault } = getValueForParameter(parameter);
@@ -1426,14 +1469,14 @@ const getValueForParameter = (parameter) => {
1426
1469
  if (typeof defaultValue !== 'undefined') {
1427
1470
  return { value: stringifyValue(defaultValue), isDefault: true };
1428
1471
  }
1429
- const examples = (_a = parameter.examples) !== null && _a !== void 0 ? _a : [];
1430
- if (examples.length > 0) {
1431
- return { value: exampleValue(examples[0]) };
1432
- }
1433
- const enums = (_c = (_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.enum) !== null && _c !== void 0 ? _c : [];
1472
+ const enums = (_b = (_a = parameter.schema) === null || _a === void 0 ? void 0 : _a.enum) !== null && _b !== void 0 ? _b : [];
1434
1473
  if (enums.length > 0) {
1435
1474
  return { value: stringifyValue(enums[0]) };
1436
1475
  }
1476
+ const examples = (_c = parameter.examples) !== null && _c !== void 0 ? _c : [];
1477
+ if (examples.length > 0) {
1478
+ return { value: exampleValue(examples[0]) };
1479
+ }
1437
1480
  return { value: '' };
1438
1481
  };
1439
1482
  const getInitialValueForParameter = (parameter) => {
@@ -1489,11 +1532,19 @@ const ParameterEditor = ({ parameter, value, onChange, isOptional, onChangeOptio
1489
1532
  const examples = exampleOptions(parameter);
1490
1533
  const selectedExample = (_a = examples === null || examples === void 0 ? void 0 : examples.find(e => e.value === value)) !== null && _a !== void 0 ? _a : selectExampleOption;
1491
1534
  const parameterDisplayName = `${parameter.name}${parameter.required ? '*' : ''}`;
1535
+ const encodedValue = React__namespace.useMemo(() => {
1536
+ if (!value || !parameterValueOptions)
1537
+ return value || '';
1538
+ const matchingOption = parameterValueOptions.find(opt => {
1539
+ return String(decodeSafeSelectorValue(opt.value)) === value;
1540
+ });
1541
+ return matchingOption ? String(matchingOption.value) : value;
1542
+ }, [value, parameterValueOptions]);
1492
1543
  const requiredButEmpty = validate && parameter.required && !value;
1493
1544
  return (React__namespace.createElement(React__namespace.Fragment, null,
1494
1545
  React__namespace.createElement(mosaic.Text, { as: "label", "aria-hidden": "true", "data-testid": "param-label", htmlFor: inputId, fontSize: "base" }, parameterDisplayName),
1495
1546
  React__namespace.createElement(mosaic.Text, { mx: 3 }, ":"),
1496
- React__namespace.createElement("div", null, parameterValueOptions ? (React__namespace.createElement(mosaic.Select, { flex: 1, "aria-label": parameter.name, options: parameterValueOptions, value: value || '', onChange: onChange, placeholder: getPlaceholderForSelectedParameter(parameter) })) : (React__namespace.createElement(mosaic.Flex, { flex: 1 },
1547
+ React__namespace.createElement("div", null, parameterValueOptions ? (React__namespace.createElement(mosaic.Select, { flex: 1, "aria-label": parameter.name, options: parameterValueOptions, value: encodedValue, onChange: val => onChange && onChange(String(decodeSafeSelectorValue(val))), placeholder: getPlaceholderForSelectedParameter(parameter) })) : (React__namespace.createElement(mosaic.Flex, { flex: 1 },
1497
1548
  React__namespace.createElement(mosaic.Input, { id: inputId, "aria-label": parameter.name, appearance: requiredButEmpty ? 'default' : 'minimal', flex: 1, placeholder: getPlaceholderForParameter(parameter), type: ((_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.type) === 'number' ? 'number' : 'text', required: true, intent: requiredButEmpty ? 'danger' : 'default', value: value || '', onChange: e => onChange && onChange(e.currentTarget.value) }),
1498
1549
  examples && (React__namespace.createElement(mosaic.Select, { "aria-label": `${parameter.name}-select`, flex: 1, value: selectedExample.value, options: examples, onChange: onChange }))))),
1499
1550
  canChangeOptional && !parameter.required && (React__namespace.createElement(React__namespace.Fragment, null,
@@ -2348,10 +2399,17 @@ ServersDropdown.displayName = 'ServersDropdown';
2348
2399
 
2349
2400
  const VariableEditor = ({ variable, value, onChange }) => {
2350
2401
  const inputId = useUniqueId(`id_${variable.name}_`);
2402
+ const encodedOptions = React__namespace.useMemo(() => (variable.enum ? variable.enum.map(s => ({ value: encodeSafeSelectorValue(s), label: String(s) })) : []), [variable.enum]);
2403
+ const encodedValue = React__namespace.useMemo(() => {
2404
+ if (!value || !variable.enum)
2405
+ return value || variable.default;
2406
+ const matchingOption = encodedOptions.find(opt => decodeSafeSelectorValue(String(opt.value)) === value);
2407
+ return matchingOption ? String(matchingOption.value) : value;
2408
+ }, [value, variable.enum, variable.default, encodedOptions]);
2351
2409
  return (React__namespace.createElement(React__namespace.Fragment, null,
2352
2410
  React__namespace.createElement(mosaic.Text, { as: "label", "aria-hidden": "true", "data-testid": "param-label", htmlFor: inputId, fontSize: "base" }, variable.name),
2353
2411
  React__namespace.createElement(mosaic.Text, { mx: 3 }, ":"),
2354
- React__namespace.createElement("div", null, variable.enum ? (React__namespace.createElement(mosaic.Select, { flex: 1, "aria-label": variable.name, options: variable.enum.map(s => ({ value: s })), value: value || variable.default, onChange: onChange })) : (React__namespace.createElement(mosaic.Flex, { flex: 1 },
2412
+ React__namespace.createElement("div", null, variable.enum ? (React__namespace.createElement(mosaic.Select, { flex: 1, "aria-label": variable.name, options: encodedOptions, value: encodedValue, onChange: val => onChange && onChange(decodeSafeSelectorValue(String(val))) })) : (React__namespace.createElement(mosaic.Flex, { flex: 1 },
2355
2413
  React__namespace.createElement(mosaic.Input, { id: inputId, "aria-label": variable.name, appearance: 'minimal', flex: 1, placeholder: variable.default, type: "text", required: true, intent: 'default', value: value || '', onChange: e => onChange && onChange(e.currentTarget.value) }))))));
2356
2414
  };
2357
2415
 
@@ -3517,7 +3575,7 @@ function findFirstNode(items) {
3517
3575
  return;
3518
3576
  }
3519
3577
  function isDivider(item) {
3520
- return Object.keys(item).length === 1 && 'title' in item;
3578
+ return Object.keys(item).length === 2 && 'title' in item && 'index' in item;
3521
3579
  }
3522
3580
  function isGroup(item) {
3523
3581
  return Object.keys(item).length >= 2 && 'title' in item && 'items' in item;
@@ -3529,13 +3587,33 @@ function isNode(item) {
3529
3587
  return 'title' in item && 'slug' in item && 'id' in item && 'meta' in item && 'type' in item;
3530
3588
  }
3531
3589
  function isExternalLink(item) {
3532
- return Object.keys(item).length === 2 && 'title' in item && 'url' in item;
3590
+ return Object.keys(item).length === 3 && 'title' in item && 'url' in item && 'index' in item;
3533
3591
  }
3534
3592
 
3535
- const ActiveIdContext = React__namespace.createContext(undefined);
3593
+ const ActiveItemContext = React__namespace.createContext({
3594
+ activeId: undefined,
3595
+ lastActiveIndex: '',
3596
+ setLastActiveIndex: () => { },
3597
+ });
3536
3598
  const LinkContext = React__namespace.createContext(undefined);
3537
3599
  LinkContext.displayName = 'LinkContext';
3538
3600
  const TableOfContents = React__namespace.memo(({ tree, activeId, Link, maxDepthOpenByDefault, externalScrollbar = false, isInResponsiveMode = false, onLinkClick, }) => {
3601
+ const [lastActiveIndex, setLastActiveIndex] = React.useState('');
3602
+ const value = React__namespace.useMemo(() => ({
3603
+ lastActiveIndex,
3604
+ setLastActiveIndex,
3605
+ activeId,
3606
+ }), [lastActiveIndex, activeId]);
3607
+ const updateTocTree = React__namespace.useCallback((arr, parentId) => {
3608
+ return arr.map((item, key) => {
3609
+ let newItem = Object.assign(Object.assign({}, item), { index: parentId + key + '-' });
3610
+ if (isGroup(item) || isNodeGroup(item)) {
3611
+ newItem.items = updateTocTree(item.items, parentId + key + '-');
3612
+ }
3613
+ return newItem;
3614
+ });
3615
+ }, []);
3616
+ const updatedTree = updateTocTree(tree, '');
3539
3617
  const container = React__namespace.useRef(null);
3540
3618
  const child = React__namespace.useRef(null);
3541
3619
  const firstRender = useFirstRender();
@@ -3555,18 +3633,30 @@ const TableOfContents = React__namespace.memo(({ tree, activeId, Link, maxDepthO
3555
3633
  return (React__namespace.createElement(mosaic.Box, { ref: container, w: "full", bg: isInResponsiveMode ? 'canvas' : 'canvas-100', overflowY: "auto" },
3556
3634
  React__namespace.createElement(mosaic.Box, { ref: child, my: 3 },
3557
3635
  React__namespace.createElement(LinkContext.Provider, { value: Link },
3558
- React__namespace.createElement(ActiveIdContext.Provider, { value: activeId }, tree.map((item, key) => {
3559
- if (isDivider(item)) {
3560
- return React__namespace.createElement(Divider, { key: key, item: item, isInResponsiveMode: isInResponsiveMode });
3561
- }
3562
- return (React__namespace.createElement(GroupItem, { key: key, item: item, depth: 0, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3563
- }))))));
3636
+ React__namespace.createElement(ActiveItemContext.Provider, { value: value },
3637
+ React__namespace.createElement(TOCContainer, { updatedTree: updatedTree }, updatedTree.map((item, key) => {
3638
+ if (isDivider(item)) {
3639
+ return React__namespace.createElement(Divider, { key: key, item: item, isInResponsiveMode: isInResponsiveMode });
3640
+ }
3641
+ return (React__namespace.createElement(GroupItem, { key: key, item: item, depth: 0, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3642
+ })))))));
3564
3643
  });
3565
3644
  TableOfContents.displayName = 'TableOfContents';
3566
3645
  const Divider = React__namespace.memo(({ item, isInResponsiveMode = false }) => {
3567
3646
  return (React__namespace.createElement(mosaic.Box, { pl: 4, mb: 2, mt: 6, textTransform: "uppercase", fontSize: isInResponsiveMode ? 'lg' : 'sm', lineHeight: "relaxed", letterSpacing: "wide", fontWeight: "bold" }, item.title));
3568
3647
  });
3569
3648
  Divider.displayName = 'Divider';
3649
+ const TOCContainer = React__namespace.memo(({ children, updatedTree }) => {
3650
+ const { setLastActiveIndex } = React__namespace.useContext(ActiveItemContext);
3651
+ React__namespace.useEffect(() => {
3652
+ const firstNode = findFirstNode(updatedTree);
3653
+ if (firstNode) {
3654
+ setLastActiveIndex(firstNode.index);
3655
+ }
3656
+ }, []);
3657
+ return React__namespace.createElement(mosaic.Box, null, children);
3658
+ });
3659
+ TOCContainer.displayName = 'TOCContainer';
3570
3660
  const GroupItem = React__namespace.memo(({ item, depth, maxDepthOpenByDefault, isInResponsiveMode, onLinkClick }) => {
3571
3661
  if (isExternalLink(item)) {
3572
3662
  return (React__namespace.createElement(mosaic.Box, { as: "a", href: item.url, target: "_blank", rel: "noopener noreferrer", display: "block" },
@@ -3584,9 +3674,24 @@ const GroupItem = React__namespace.memo(({ item, depth, maxDepthOpenByDefault, i
3584
3674
  });
3585
3675
  GroupItem.displayName = 'GroupItem';
3586
3676
  const Group = React__namespace.memo(({ depth, item, maxDepthOpenByDefault, isInResponsiveMode, onLinkClick = () => { } }) => {
3587
- const activeId = React__namespace.useContext(ActiveIdContext);
3677
+ const { activeId, lastActiveIndex } = React__namespace.useContext(ActiveItemContext);
3588
3678
  const [isOpen, setIsOpen] = React__namespace.useState(() => isGroupOpenByDefault(depth, item, activeId, maxDepthOpenByDefault));
3589
- const hasActive = !!activeId && hasActiveItem(item.items, activeId);
3679
+ const isActiveGroup = React__namespace.useCallback((items, activeId, contextIndex) => {
3680
+ return items.some(element => {
3681
+ const hasSlugOrId = 'slug' in element || 'id' in element;
3682
+ const hasItems = 'items' in element && Array.isArray(element.items);
3683
+ if (!hasSlugOrId && !hasItems)
3684
+ return false;
3685
+ if (activeId &&
3686
+ 'index' in element &&
3687
+ (element.slug === activeId || element.id === activeId) &&
3688
+ element.index === contextIndex) {
3689
+ return true;
3690
+ }
3691
+ return hasItems ? isActiveGroup(element.items, activeId, contextIndex) : false;
3692
+ });
3693
+ }, []);
3694
+ const hasActive = isActiveGroup(item.items, activeId, lastActiveIndex);
3590
3695
  React__namespace.useEffect(() => {
3591
3696
  const openByDefault = isGroupOpenByDefault(depth, item, activeId, maxDepthOpenByDefault);
3592
3697
  if (isOpen !== openByDefault) {
@@ -3636,8 +3741,10 @@ const Item = React__namespace.memo(({ depth, isActive, id, title, meta, icon, is
3636
3741
  });
3637
3742
  Item.displayName = 'Item';
3638
3743
  const Node = React__namespace.memo(({ item, depth, meta, showAsActive, isInResponsiveMode, onClick, onLinkClick = () => { } }) => {
3639
- const activeId = React__namespace.useContext(ActiveIdContext);
3640
- const isActive = activeId === item.slug || activeId === item.id;
3744
+ const { activeId, lastActiveIndex, setLastActiveIndex } = React__namespace.useContext(ActiveItemContext);
3745
+ const { index } = item;
3746
+ const isSlugMatched = activeId === item.slug || activeId === item.id;
3747
+ const isActive = lastActiveIndex === index && isSlugMatched;
3641
3748
  const LinkComponent = React__namespace.useContext(LinkContext);
3642
3749
  const handleClick = (e) => {
3643
3750
  if (isActive) {
@@ -3645,6 +3752,7 @@ const Node = React__namespace.memo(({ item, depth, meta, showAsActive, isInRespo
3645
3752
  e.preventDefault();
3646
3753
  }
3647
3754
  else {
3755
+ setLastActiveIndex(index);
3648
3756
  onLinkClick();
3649
3757
  }
3650
3758
  if (onClick) {
@@ -3652,7 +3760,7 @@ const Node = React__namespace.memo(({ item, depth, meta, showAsActive, isInRespo
3652
3760
  }
3653
3761
  };
3654
3762
  return (React__namespace.createElement(mosaic.Box, { as: LinkComponent, to: resolveRelativeLink(item.slug), display: "block", textDecoration: "no-underline", className: "ElementsTableOfContentsItem" },
3655
- React__namespace.createElement(Item, { id: getHtmlIdFromItemId(item.slug || item.id), isActive: isActive || showAsActive, depth: depth, title: item.title, icon: NODE_TYPE_TITLE_ICON[item.type] && (React__namespace.createElement(mosaic.Box, { as: mosaic.Icon, color: NODE_TYPE_ICON_COLOR[item.type], icon: NODE_TYPE_TITLE_ICON[item.type] })), meta: meta, isInResponsiveMode: isInResponsiveMode, onClick: handleClick })));
3763
+ React__namespace.createElement(Item, { id: getHtmlIdFromItemId(item.slug || item.id), isActive: isActive || showAsActive, depth: depth, title: item.title, icon: NODE_TYPE_TITLE_ICON[item.type] && (React__namespace.createElement(mosaic.Box, { as: mosaic.Icon, color: NODE_TYPE_ICON_COLOR[item.type], icon: NODE_TYPE_TITLE_ICON[item.type] })), meta: meta, isInResponsiveMode: isInResponsiveMode, onClick: e => handleClick(e) })));
3656
3764
  });
3657
3765
  Node.displayName = 'Node';
3658
3766
  const Version = ({ value }) => {
package/index.mjs CHANGED
@@ -1340,8 +1340,50 @@ const booleanOptions = [
1340
1340
  { label: 'False', value: 'false' },
1341
1341
  { label: 'True', value: 'true' },
1342
1342
  ];
1343
+ function encodeSafeSelectorValue(value) {
1344
+ if (typeof value === 'number') {
1345
+ return value;
1346
+ }
1347
+ const hasSpecialChars = /["'\[\]\\(){}]/.test(value);
1348
+ if (!hasSpecialChars) {
1349
+ return value;
1350
+ }
1351
+ try {
1352
+ return 'b64:' + btoa(value);
1353
+ }
1354
+ catch (e) {
1355
+ return 'enc:' + encodeURIComponent(value);
1356
+ }
1357
+ }
1358
+ function decodeSafeSelectorValue(value) {
1359
+ if (typeof value === 'number') {
1360
+ return value;
1361
+ }
1362
+ if (value.startsWith('b64:')) {
1363
+ try {
1364
+ return atob(value.substring(4));
1365
+ }
1366
+ catch (e) {
1367
+ return value;
1368
+ }
1369
+ }
1370
+ if (value.startsWith('enc:')) {
1371
+ try {
1372
+ return decodeURIComponent(value.substring(4));
1373
+ }
1374
+ catch (e) {
1375
+ return value;
1376
+ }
1377
+ }
1378
+ return value;
1379
+ }
1343
1380
  function enumOptions(enumValues, required) {
1344
- const options = map(enumValues, v => ({ value: typeof v === 'number' ? v : String(v) }));
1381
+ const options = map(enumValues, v => {
1382
+ var _a;
1383
+ const stringValue = typeof v === 'object' && v !== null ? (_a = safeStringify(v)) !== null && _a !== void 0 ? _a : String(v) : typeof v === 'number' ? v : String(v);
1384
+ const safeValue = encodeSafeSelectorValue(stringValue);
1385
+ return { value: safeValue, label: String(stringValue) };
1386
+ });
1345
1387
  return required ? options : [{ label: 'Not Set', value: '' }, ...options];
1346
1388
  }
1347
1389
  function parameterOptions(parameter) {
@@ -1371,15 +1413,16 @@ function parameterSupportsFileUpload(parameter) {
1371
1413
  ((_c = parameter.schema) === null || _c === void 0 ? void 0 : _c.contentMediaType) === 'application/octet-stream'));
1372
1414
  }
1373
1415
  function stringifyValue(value) {
1374
- return typeof value === 'object' ? JSON.stringify(value) : escapeQuotes(String(value));
1416
+ var _a;
1417
+ if (typeof value === 'object' && value !== null) {
1418
+ return (_a = safeStringify(value)) !== null && _a !== void 0 ? _a : String(value);
1419
+ }
1420
+ return String(value);
1375
1421
  }
1376
1422
  function exampleValue(example) {
1377
1423
  const value = 'value' in example ? example.value : example.externalValue;
1378
1424
  return stringifyValue(value);
1379
1425
  }
1380
- function escapeQuotes(value) {
1381
- return value.replace(/"/g, '\\"');
1382
- }
1383
1426
  function getPlaceholderForParameter(parameter) {
1384
1427
  var _a, _b;
1385
1428
  const { value: parameterValue, isDefault } = getValueForParameter(parameter);
@@ -1405,14 +1448,14 @@ const getValueForParameter = (parameter) => {
1405
1448
  if (typeof defaultValue !== 'undefined') {
1406
1449
  return { value: stringifyValue(defaultValue), isDefault: true };
1407
1450
  }
1408
- const examples = (_a = parameter.examples) !== null && _a !== void 0 ? _a : [];
1409
- if (examples.length > 0) {
1410
- return { value: exampleValue(examples[0]) };
1411
- }
1412
- const enums = (_c = (_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.enum) !== null && _c !== void 0 ? _c : [];
1451
+ const enums = (_b = (_a = parameter.schema) === null || _a === void 0 ? void 0 : _a.enum) !== null && _b !== void 0 ? _b : [];
1413
1452
  if (enums.length > 0) {
1414
1453
  return { value: stringifyValue(enums[0]) };
1415
1454
  }
1455
+ const examples = (_c = parameter.examples) !== null && _c !== void 0 ? _c : [];
1456
+ if (examples.length > 0) {
1457
+ return { value: exampleValue(examples[0]) };
1458
+ }
1416
1459
  return { value: '' };
1417
1460
  };
1418
1461
  const getInitialValueForParameter = (parameter) => {
@@ -1468,11 +1511,19 @@ const ParameterEditor = ({ parameter, value, onChange, isOptional, onChangeOptio
1468
1511
  const examples = exampleOptions(parameter);
1469
1512
  const selectedExample = (_a = examples === null || examples === void 0 ? void 0 : examples.find(e => e.value === value)) !== null && _a !== void 0 ? _a : selectExampleOption;
1470
1513
  const parameterDisplayName = `${parameter.name}${parameter.required ? '*' : ''}`;
1514
+ const encodedValue = React.useMemo(() => {
1515
+ if (!value || !parameterValueOptions)
1516
+ return value || '';
1517
+ const matchingOption = parameterValueOptions.find(opt => {
1518
+ return String(decodeSafeSelectorValue(opt.value)) === value;
1519
+ });
1520
+ return matchingOption ? String(matchingOption.value) : value;
1521
+ }, [value, parameterValueOptions]);
1471
1522
  const requiredButEmpty = validate && parameter.required && !value;
1472
1523
  return (React.createElement(React.Fragment, null,
1473
1524
  React.createElement(Text, { as: "label", "aria-hidden": "true", "data-testid": "param-label", htmlFor: inputId, fontSize: "base" }, parameterDisplayName),
1474
1525
  React.createElement(Text, { mx: 3 }, ":"),
1475
- React.createElement("div", null, parameterValueOptions ? (React.createElement(Select, { flex: 1, "aria-label": parameter.name, options: parameterValueOptions, value: value || '', onChange: onChange, placeholder: getPlaceholderForSelectedParameter(parameter) })) : (React.createElement(Flex, { flex: 1 },
1526
+ React.createElement("div", null, parameterValueOptions ? (React.createElement(Select, { flex: 1, "aria-label": parameter.name, options: parameterValueOptions, value: encodedValue, onChange: val => onChange && onChange(String(decodeSafeSelectorValue(val))), placeholder: getPlaceholderForSelectedParameter(parameter) })) : (React.createElement(Flex, { flex: 1 },
1476
1527
  React.createElement(Input, { id: inputId, "aria-label": parameter.name, appearance: requiredButEmpty ? 'default' : 'minimal', flex: 1, placeholder: getPlaceholderForParameter(parameter), type: ((_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.type) === 'number' ? 'number' : 'text', required: true, intent: requiredButEmpty ? 'danger' : 'default', value: value || '', onChange: e => onChange && onChange(e.currentTarget.value) }),
1477
1528
  examples && (React.createElement(Select, { "aria-label": `${parameter.name}-select`, flex: 1, value: selectedExample.value, options: examples, onChange: onChange }))))),
1478
1529
  canChangeOptional && !parameter.required && (React.createElement(React.Fragment, null,
@@ -2327,10 +2378,17 @@ ServersDropdown.displayName = 'ServersDropdown';
2327
2378
 
2328
2379
  const VariableEditor = ({ variable, value, onChange }) => {
2329
2380
  const inputId = useUniqueId(`id_${variable.name}_`);
2381
+ const encodedOptions = React.useMemo(() => (variable.enum ? variable.enum.map(s => ({ value: encodeSafeSelectorValue(s), label: String(s) })) : []), [variable.enum]);
2382
+ const encodedValue = React.useMemo(() => {
2383
+ if (!value || !variable.enum)
2384
+ return value || variable.default;
2385
+ const matchingOption = encodedOptions.find(opt => decodeSafeSelectorValue(String(opt.value)) === value);
2386
+ return matchingOption ? String(matchingOption.value) : value;
2387
+ }, [value, variable.enum, variable.default, encodedOptions]);
2330
2388
  return (React.createElement(React.Fragment, null,
2331
2389
  React.createElement(Text, { as: "label", "aria-hidden": "true", "data-testid": "param-label", htmlFor: inputId, fontSize: "base" }, variable.name),
2332
2390
  React.createElement(Text, { mx: 3 }, ":"),
2333
- React.createElement("div", null, variable.enum ? (React.createElement(Select, { flex: 1, "aria-label": variable.name, options: variable.enum.map(s => ({ value: s })), value: value || variable.default, onChange: onChange })) : (React.createElement(Flex, { flex: 1 },
2391
+ React.createElement("div", null, variable.enum ? (React.createElement(Select, { flex: 1, "aria-label": variable.name, options: encodedOptions, value: encodedValue, onChange: val => onChange && onChange(decodeSafeSelectorValue(String(val))) })) : (React.createElement(Flex, { flex: 1 },
2334
2392
  React.createElement(Input, { id: inputId, "aria-label": variable.name, appearance: 'minimal', flex: 1, placeholder: variable.default, type: "text", required: true, intent: 'default', value: value || '', onChange: e => onChange && onChange(e.currentTarget.value) }))))));
2335
2393
  };
2336
2394
 
@@ -3496,7 +3554,7 @@ function findFirstNode(items) {
3496
3554
  return;
3497
3555
  }
3498
3556
  function isDivider(item) {
3499
- return Object.keys(item).length === 1 && 'title' in item;
3557
+ return Object.keys(item).length === 2 && 'title' in item && 'index' in item;
3500
3558
  }
3501
3559
  function isGroup(item) {
3502
3560
  return Object.keys(item).length >= 2 && 'title' in item && 'items' in item;
@@ -3508,13 +3566,33 @@ function isNode(item) {
3508
3566
  return 'title' in item && 'slug' in item && 'id' in item && 'meta' in item && 'type' in item;
3509
3567
  }
3510
3568
  function isExternalLink(item) {
3511
- return Object.keys(item).length === 2 && 'title' in item && 'url' in item;
3569
+ return Object.keys(item).length === 3 && 'title' in item && 'url' in item && 'index' in item;
3512
3570
  }
3513
3571
 
3514
- const ActiveIdContext = React.createContext(undefined);
3572
+ const ActiveItemContext = React.createContext({
3573
+ activeId: undefined,
3574
+ lastActiveIndex: '',
3575
+ setLastActiveIndex: () => { },
3576
+ });
3515
3577
  const LinkContext = React.createContext(undefined);
3516
3578
  LinkContext.displayName = 'LinkContext';
3517
3579
  const TableOfContents = React.memo(({ tree, activeId, Link, maxDepthOpenByDefault, externalScrollbar = false, isInResponsiveMode = false, onLinkClick, }) => {
3580
+ const [lastActiveIndex, setLastActiveIndex] = useState('');
3581
+ const value = React.useMemo(() => ({
3582
+ lastActiveIndex,
3583
+ setLastActiveIndex,
3584
+ activeId,
3585
+ }), [lastActiveIndex, activeId]);
3586
+ const updateTocTree = React.useCallback((arr, parentId) => {
3587
+ return arr.map((item, key) => {
3588
+ let newItem = Object.assign(Object.assign({}, item), { index: parentId + key + '-' });
3589
+ if (isGroup(item) || isNodeGroup(item)) {
3590
+ newItem.items = updateTocTree(item.items, parentId + key + '-');
3591
+ }
3592
+ return newItem;
3593
+ });
3594
+ }, []);
3595
+ const updatedTree = updateTocTree(tree, '');
3518
3596
  const container = React.useRef(null);
3519
3597
  const child = React.useRef(null);
3520
3598
  const firstRender = useFirstRender();
@@ -3534,18 +3612,30 @@ const TableOfContents = React.memo(({ tree, activeId, Link, maxDepthOpenByDefaul
3534
3612
  return (React.createElement(Box, { ref: container, w: "full", bg: isInResponsiveMode ? 'canvas' : 'canvas-100', overflowY: "auto" },
3535
3613
  React.createElement(Box, { ref: child, my: 3 },
3536
3614
  React.createElement(LinkContext.Provider, { value: Link },
3537
- React.createElement(ActiveIdContext.Provider, { value: activeId }, tree.map((item, key) => {
3538
- if (isDivider(item)) {
3539
- return React.createElement(Divider, { key: key, item: item, isInResponsiveMode: isInResponsiveMode });
3540
- }
3541
- return (React.createElement(GroupItem, { key: key, item: item, depth: 0, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3542
- }))))));
3615
+ React.createElement(ActiveItemContext.Provider, { value: value },
3616
+ React.createElement(TOCContainer, { updatedTree: updatedTree }, updatedTree.map((item, key) => {
3617
+ if (isDivider(item)) {
3618
+ return React.createElement(Divider, { key: key, item: item, isInResponsiveMode: isInResponsiveMode });
3619
+ }
3620
+ return (React.createElement(GroupItem, { key: key, item: item, depth: 0, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3621
+ })))))));
3543
3622
  });
3544
3623
  TableOfContents.displayName = 'TableOfContents';
3545
3624
  const Divider = React.memo(({ item, isInResponsiveMode = false }) => {
3546
3625
  return (React.createElement(Box, { pl: 4, mb: 2, mt: 6, textTransform: "uppercase", fontSize: isInResponsiveMode ? 'lg' : 'sm', lineHeight: "relaxed", letterSpacing: "wide", fontWeight: "bold" }, item.title));
3547
3626
  });
3548
3627
  Divider.displayName = 'Divider';
3628
+ const TOCContainer = React.memo(({ children, updatedTree }) => {
3629
+ const { setLastActiveIndex } = React.useContext(ActiveItemContext);
3630
+ React.useEffect(() => {
3631
+ const firstNode = findFirstNode(updatedTree);
3632
+ if (firstNode) {
3633
+ setLastActiveIndex(firstNode.index);
3634
+ }
3635
+ }, []);
3636
+ return React.createElement(Box, null, children);
3637
+ });
3638
+ TOCContainer.displayName = 'TOCContainer';
3549
3639
  const GroupItem = React.memo(({ item, depth, maxDepthOpenByDefault, isInResponsiveMode, onLinkClick }) => {
3550
3640
  if (isExternalLink(item)) {
3551
3641
  return (React.createElement(Box, { as: "a", href: item.url, target: "_blank", rel: "noopener noreferrer", display: "block" },
@@ -3563,9 +3653,24 @@ const GroupItem = React.memo(({ item, depth, maxDepthOpenByDefault, isInResponsi
3563
3653
  });
3564
3654
  GroupItem.displayName = 'GroupItem';
3565
3655
  const Group = React.memo(({ depth, item, maxDepthOpenByDefault, isInResponsiveMode, onLinkClick = () => { } }) => {
3566
- const activeId = React.useContext(ActiveIdContext);
3656
+ const { activeId, lastActiveIndex } = React.useContext(ActiveItemContext);
3567
3657
  const [isOpen, setIsOpen] = React.useState(() => isGroupOpenByDefault(depth, item, activeId, maxDepthOpenByDefault));
3568
- const hasActive = !!activeId && hasActiveItem(item.items, activeId);
3658
+ const isActiveGroup = React.useCallback((items, activeId, contextIndex) => {
3659
+ return items.some(element => {
3660
+ const hasSlugOrId = 'slug' in element || 'id' in element;
3661
+ const hasItems = 'items' in element && Array.isArray(element.items);
3662
+ if (!hasSlugOrId && !hasItems)
3663
+ return false;
3664
+ if (activeId &&
3665
+ 'index' in element &&
3666
+ (element.slug === activeId || element.id === activeId) &&
3667
+ element.index === contextIndex) {
3668
+ return true;
3669
+ }
3670
+ return hasItems ? isActiveGroup(element.items, activeId, contextIndex) : false;
3671
+ });
3672
+ }, []);
3673
+ const hasActive = isActiveGroup(item.items, activeId, lastActiveIndex);
3569
3674
  React.useEffect(() => {
3570
3675
  const openByDefault = isGroupOpenByDefault(depth, item, activeId, maxDepthOpenByDefault);
3571
3676
  if (isOpen !== openByDefault) {
@@ -3615,8 +3720,10 @@ const Item = React.memo(({ depth, isActive, id, title, meta, icon, isInResponsiv
3615
3720
  });
3616
3721
  Item.displayName = 'Item';
3617
3722
  const Node = React.memo(({ item, depth, meta, showAsActive, isInResponsiveMode, onClick, onLinkClick = () => { } }) => {
3618
- const activeId = React.useContext(ActiveIdContext);
3619
- const isActive = activeId === item.slug || activeId === item.id;
3723
+ const { activeId, lastActiveIndex, setLastActiveIndex } = React.useContext(ActiveItemContext);
3724
+ const { index } = item;
3725
+ const isSlugMatched = activeId === item.slug || activeId === item.id;
3726
+ const isActive = lastActiveIndex === index && isSlugMatched;
3620
3727
  const LinkComponent = React.useContext(LinkContext);
3621
3728
  const handleClick = (e) => {
3622
3729
  if (isActive) {
@@ -3624,6 +3731,7 @@ const Node = React.memo(({ item, depth, meta, showAsActive, isInResponsiveMode,
3624
3731
  e.preventDefault();
3625
3732
  }
3626
3733
  else {
3734
+ setLastActiveIndex(index);
3627
3735
  onLinkClick();
3628
3736
  }
3629
3737
  if (onClick) {
@@ -3631,7 +3739,7 @@ const Node = React.memo(({ item, depth, meta, showAsActive, isInResponsiveMode,
3631
3739
  }
3632
3740
  };
3633
3741
  return (React.createElement(Box, { as: LinkComponent, to: resolveRelativeLink(item.slug), display: "block", textDecoration: "no-underline", className: "ElementsTableOfContentsItem" },
3634
- React.createElement(Item, { id: getHtmlIdFromItemId(item.slug || item.id), isActive: isActive || showAsActive, depth: depth, title: item.title, icon: NODE_TYPE_TITLE_ICON[item.type] && (React.createElement(Box, { as: Icon, color: NODE_TYPE_ICON_COLOR[item.type], icon: NODE_TYPE_TITLE_ICON[item.type] })), meta: meta, isInResponsiveMode: isInResponsiveMode, onClick: handleClick })));
3742
+ React.createElement(Item, { id: getHtmlIdFromItemId(item.slug || item.id), isActive: isActive || showAsActive, depth: depth, title: item.title, icon: NODE_TYPE_TITLE_ICON[item.type] && (React.createElement(Box, { as: Icon, color: NODE_TYPE_ICON_COLOR[item.type], icon: NODE_TYPE_TITLE_ICON[item.type] })), meta: meta, isInResponsiveMode: isInResponsiveMode, onClick: e => handleClick(e) })));
3635
3743
  });
3636
3744
  Node.displayName = 'Node';
3637
3745
  const Version = ({ value }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stoplight/elements-core",
3
- "version": "9.0.12",
3
+ "version": "9.0.13-alpha-0.1",
4
4
  "main": "./index.js",
5
5
  "sideEffects": [
6
6
  "web-components.min.js",