@xh/hoist 86.1.0 → 86.2.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.
Files changed (47) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/admin/AdminJsonDisplay.ts +31 -0
  3. package/admin/App.scss +3 -11
  4. package/admin/jsonsearch/impl/JsonSearchImplModel.ts +4 -4
  5. package/admin/tabs/cluster/instances/connpool/ConnPoolMonitorPanel.ts +3 -11
  6. package/admin/tabs/cluster/instances/services/DetailsPanel.ts +2 -13
  7. package/admin/tabs/cluster/objects/DetailPanel.ts +3 -12
  8. package/build/types/admin/AdminJsonDisplay.d.ts +7 -0
  9. package/build/types/admin/jsonsearch/impl/JsonSearchImplModel.d.ts +2 -2
  10. package/build/types/cmp/grid/Grid.d.ts +6 -3
  11. package/build/types/cmp/grid/GridModel.d.ts +11 -0
  12. package/build/types/cmp/grid/filter/GridFilterFieldSpec.d.ts +24 -6
  13. package/build/types/desktop/cmp/button/grid/ExpandToLevelButton.d.ts +2 -1
  14. package/build/types/desktop/cmp/input/CodeInput.d.ts +7 -0
  15. package/build/types/desktop/cmp/input/NumberInput.d.ts +1 -1
  16. package/build/types/desktop/cmp/input/impl/CalcWindowedMenuWidth.d.ts +20 -0
  17. package/build/types/desktop/cmp/rest/impl/RestFormModel.d.ts +1 -1
  18. package/build/types/format/FormatNumber.d.ts +11 -3
  19. package/build/types/icon/Icon.d.ts +4 -3
  20. package/build/types/mobile/cmp/button/grid/ExpandToLevelButton.d.ts +2 -1
  21. package/build/types/mobile/cmp/input/NumberInput.d.ts +1 -1
  22. package/cmp/grid/Grid.ts +17 -0
  23. package/cmp/grid/GridModel.ts +17 -5
  24. package/cmp/grid/filter/GridFilterFieldSpec.ts +28 -5
  25. package/cmp/grid/impl/MenuSupport.ts +2 -4
  26. package/desktop/cmp/button/grid/ExpandToLevelButton.ts +4 -5
  27. package/desktop/cmp/grid/editors/NumberEditor.ts +16 -17
  28. package/desktop/cmp/grid/editors/SelectEditor.ts +15 -10
  29. package/desktop/cmp/grid/impl/filter/GridFilterDialog.ts +2 -2
  30. package/desktop/cmp/grid/impl/filter/headerfilter/values/ValuesTabModel.ts +46 -7
  31. package/desktop/cmp/input/CheckboxButton.ts +21 -1
  32. package/desktop/cmp/input/CodeInput.ts +20 -3
  33. package/desktop/cmp/input/NumberInput.ts +1 -1
  34. package/desktop/cmp/input/Select.ts +13 -2
  35. package/desktop/cmp/input/impl/CalcWindowedMenuWidth.ts +106 -0
  36. package/desktop/cmp/rest/impl/RestFormField.ts +1 -1
  37. package/desktop/cmp/tab/Tabs.scss +72 -25
  38. package/format/FormatNumber.ts +69 -32
  39. package/icon/Icon.scss +13 -0
  40. package/icon/Icon.ts +4 -3
  41. package/icon/impl/IconHtml.ts +1 -1
  42. package/mcp/data/ts-registry.ts +1 -1
  43. package/mobile/cmp/button/grid/ExpandToLevelButton.ts +4 -5
  44. package/mobile/cmp/input/CheckboxButton.ts +18 -1
  45. package/mobile/cmp/input/NumberInput.ts +1 -1
  46. package/package.json +6 -6
  47. package/styles/vars.scss +15 -0
@@ -36,9 +36,36 @@
36
36
  }
37
37
  }
38
38
 
39
+ // Generic tab defaults - orientation-specific blocks below layer on top of these.
40
+ .bp6-tab {
41
+ color: var(--xh-tab-text-color);
42
+
43
+ &[aria-disabled='true'] {
44
+ color: var(--xh-tab-disabled-text-color);
45
+ }
46
+
47
+ // Tooltip
48
+ .bp6-popover-target {
49
+ width: 100%;
50
+ }
51
+ }
52
+
53
+ // Active/hover color override
54
+ .bp6-tab[aria-selected='true'],
55
+ .bp6-tab:not([aria-disabled='true']):hover {
56
+ .xh-tab-switcher__tab {
57
+ color: var(--xh-tab-active-text-color);
58
+ }
59
+ }
60
+
39
61
  &--top {
40
62
  padding-left: var(--xh-pad-px);
41
63
  max-width: 100%;
64
+
65
+ // Active indicator sits on the bottom edge of the tab, adjacent to the content below.
66
+ .bp6-tab[aria-selected='true'] {
67
+ box-shadow: inset 0 -3px 0 var(--xh-tab-active-text-color);
68
+ }
42
69
  }
43
70
 
44
71
  &--bottom {
@@ -56,6 +83,8 @@
56
83
  &--right {
57
84
  flex-direction: column;
58
85
  max-height: 100%;
86
+ min-width: var(--xh-tab-switcher-vertical-min-width);
87
+ padding: var(--xh-tab-switcher-vertical-padding);
59
88
 
60
89
  // Make overflow button full width
61
90
  .bp6-popover-target {
@@ -65,6 +94,49 @@
65
94
  width: 100%;
66
95
  }
67
96
  }
97
+
98
+ // Modern vertical rail: rounded, padded items with a muted resting color, a clear hover
99
+ // affordance, and a filled active "pill". Fully driven off the --xh-tab-switcher-vertical-*
100
+ // tokens so it stays light/dark-theme safe and is easily customized or disabled per-app.
101
+ .bp6-tab {
102
+ margin-bottom: var(--xh-tab-switcher-vertical-item-gap);
103
+ padding: var(--xh-tab-switcher-vertical-item-padding);
104
+ border-radius: var(--xh-tab-switcher-vertical-item-border-radius);
105
+ color: var(--xh-tab-switcher-vertical-item-text-color);
106
+ font-weight: var(--xh-tab-switcher-vertical-item-font-weight);
107
+ transition:
108
+ background-color 0.15s ease,
109
+ color 0.15s ease,
110
+ box-shadow 0.15s ease;
111
+ }
112
+
113
+ .bp6-tab:not([aria-disabled='true']):hover {
114
+ background-color: var(--xh-tab-switcher-vertical-item-hover-bg);
115
+ }
116
+
117
+ .bp6-tab[aria-selected='true'] {
118
+ background-color: var(--xh-tab-switcher-vertical-item-active-bg);
119
+ font-weight: var(--xh-tab-switcher-vertical-item-active-font-weight);
120
+ }
121
+
122
+ // Hover + active labels take the active/intent text color, re-applied to both the tab and its
123
+ // inner label so it overrides the muted resting color set on the item above.
124
+ .bp6-tab[aria-selected='true'],
125
+ .bp6-tab:not([aria-disabled='true']):hover {
126
+ color: var(--xh-tab-switcher-vertical-item-active-text-color);
127
+
128
+ .xh-tab-switcher__tab {
129
+ color: var(--xh-tab-switcher-vertical-item-active-text-color);
130
+ }
131
+ }
132
+ }
133
+
134
+ &--left {
135
+ border-right: var(--xh-tab-switcher-vertical-border);
136
+ }
137
+
138
+ &--right {
139
+ border-left: var(--xh-tab-switcher-vertical-border);
68
140
  }
69
141
 
70
142
  &__tab {
@@ -104,29 +176,4 @@
104
176
  min-height: 20px !important;
105
177
  }
106
178
  }
107
-
108
- .bp6-tab {
109
- color: var(--xh-tab-text-color);
110
-
111
- &[aria-disabled='true'] {
112
- color: var(--xh-tab-disabled-text-color);
113
- }
114
-
115
- // Tooltip
116
- .bp6-popover-target {
117
- width: 100%;
118
- }
119
- }
120
-
121
- // Active/hover color override
122
- .bp6-tab[aria-selected='true'],
123
- .bp6-tab:not([aria-disabled='true']):hover {
124
- .xh-tab-switcher__tab {
125
- color: var(--xh-tab-active-text-color);
126
- }
127
- }
128
-
129
- .bp6-tab[aria-selected='true'] {
130
- box-shadow: inset 0 -3px 0 var(--xh-tab-active-text-color);
131
- }
132
179
  }
@@ -14,6 +14,7 @@ import {
14
14
  isNil,
15
15
  isNumber,
16
16
  isString,
17
+ isUndefined,
17
18
  round
18
19
  } from 'lodash';
19
20
  import Numbro from 'numbro';
@@ -78,8 +79,9 @@ export interface NumberFormatOptions extends Omit<FormatOptions<number>, 'toolti
78
79
  omitFourDigitComma?: boolean;
79
80
 
80
81
  /**
81
- * Desired number of decimal places, or 'auto' (default) to adjust the displayed precision
82
- * automatically based on the scale of the value.
82
+ * Desired number of decimal places, 'auto' (default) to adjust the displayed precision
83
+ * automatically based on the scale of the value, or null for full, unrestricted precision
84
+ * (capped at the max supported precision, with trailing zeros trimmed).
83
85
  */
84
86
  precision?: Precision;
85
87
 
@@ -118,15 +120,23 @@ export interface NumberFormatOptions extends Omit<FormatOptions<number>, 'toolti
118
120
  *
119
121
  * e.g. `{precision:4, zeroPad:2}` will format `1.2` → "1.20" and `1.234` → "1.234"
120
122
  *
121
- * Default is true if a fixed precision is set, false if precision is 'auto'.
123
+ * Default is true if a fixed precision is set, false if precision is 'auto' or null (full).
122
124
  */
123
125
  zeroPad?: ZeroPad;
124
126
  }
125
127
 
126
128
  export interface QuantityFormatOptions extends NumberFormatOptions {
129
+ /** True to compact values \>= 1 million into units of millions (m). Default true. */
127
130
  useMillions?: boolean;
128
131
 
132
+ /** True to compact values \>= 1 billion into units of billions (b). Default true. */
129
133
  useBillions?: boolean;
134
+
135
+ /**
136
+ * True to compact to m/b units only when no precision is lost, else render the full value.
137
+ * Default false.
138
+ */
139
+ lossless?: boolean;
130
140
  }
131
141
 
132
142
  /** Config for pos/neg/neutral color classes. */
@@ -175,9 +185,16 @@ export function fmtNumber(v: number, opts?: NumberFormatOptions): ReactNode {
175
185
  } = opts ?? ({} as NumberFormatOptions);
176
186
  if (isInvalidInput(v)) return nullDisplay;
177
187
 
178
- // Ensure any non-int precision is treated as 'auto', use to default zeroPad.
179
- if (!isInteger(precision)) precision = 'auto';
180
- if (isNil(zeroPad)) zeroPad = precision != 'auto';
188
+ // Resolve precision: null means full precision, other non-integers (e.g. undefined) mean 'auto'.
189
+ const fullPrecision = precision === null;
190
+ if (fullPrecision) {
191
+ precision = MAX_NUMERIC_PRECISION;
192
+ } else if (!isInteger(precision)) {
193
+ precision = 'auto';
194
+ }
195
+
196
+ // Default zeroPad to pad only for a fixed precision - 'auto' and full precision trim zeros.
197
+ if (isNil(zeroPad)) zeroPad = precision != 'auto' && !fullPrecision;
181
198
 
182
199
  formatConfig =
183
200
  formatConfig || buildFormatConfig(v, precision, zeroPad, withCommas, omitFourDigitComma);
@@ -271,20 +288,40 @@ export function fmtQuantity(v: number, opts?: QuantityFormatOptions) {
271
288
  saveOriginal(v, opts);
272
289
  if (isInvalidInput(v)) return fmtNumber(v, opts);
273
290
 
274
- const lessThanM = Math.abs(v) < MILLION,
275
- lessThanB = Math.abs(v) < BILLION;
276
-
277
291
  defaults(opts, {
278
292
  ledger: true,
279
293
  label: true,
280
- precision: lessThanM ? 0 : 2,
281
294
  useMillions: true,
282
- useBillions: true
295
+ useBillions: true,
296
+ lossless: false
283
297
  });
284
298
 
285
- if (lessThanM || !opts.useMillions) return fmtNumber(v, opts);
286
- if (lessThanB || !opts.useBillions) return fmtMillions(v, opts);
287
- return fmtBillions(v, opts);
299
+ const absV = Math.abs(v),
300
+ lessM = absV < MILLION,
301
+ lessB = absV < BILLION,
302
+ targetPrecision = opts.precision ?? (lessM ? 0 : 2);
303
+
304
+ // Compute scaling, if any (Lossless flag may preclude).
305
+ let scale = !lessB && opts.useBillions ? BILLION : !lessM && opts.useMillions ? MILLION : null;
306
+ if (scale && opts.lossless) {
307
+ const precision = parsePrecision(absV / scale, targetPrecision),
308
+ lossy = v % (scale / 10 ** precision) !== 0;
309
+ if (lossy) scale = null;
310
+ }
311
+
312
+ // Resolve render precision (unless the caller set one).
313
+ if (isUndefined(opts.precision)) {
314
+ opts.precision = opts.lossless ? null : targetPrecision;
315
+ }
316
+
317
+ switch (scale) {
318
+ case BILLION:
319
+ return fmtBillions(v, opts);
320
+ case MILLION:
321
+ return fmtMillions(v, opts);
322
+ default:
323
+ return fmtNumber(v, opts);
324
+ }
288
325
  }
289
326
 
290
327
  /**
@@ -462,6 +499,23 @@ function calcStyleFromColorSpec(v: number, colorSpec: ColorSpec | boolean): CSSP
462
499
  return !isString(possibleStyles) ? possibleStyles : {};
463
500
  }
464
501
 
502
+ /**
503
+ * Resolve a precisionSpec to a concrete number of decimal places. A fixed precision is used as-is
504
+ * (capped at max), while 'auto' is derived from the scale of the value.
505
+ */
506
+ function parsePrecision(v: number, precisionSpec: Precision): number {
507
+ // Fixed precision - use requested, capped at max.
508
+ if (precisionSpec !== 'auto') return Math.min(precisionSpec, MAX_NUMERIC_PRECISION);
509
+
510
+ // 'auto' - derive from the scale of the value.
511
+ const absVal = Math.abs(v);
512
+ if (absVal === 0) return 2;
513
+ if (absVal < 0.01) return 6;
514
+ if (absVal < 100) return 4;
515
+ if (absVal < 10000) return 2;
516
+ return 0;
517
+ }
518
+
465
519
  function buildFormatConfig(
466
520
  v: number,
467
521
  precisionSpec: Precision,
@@ -472,24 +526,7 @@ function buildFormatConfig(
472
526
  const absVal = Math.abs(v),
473
527
  config: Numbro.Format = {};
474
528
 
475
- let precision: number;
476
- if (precisionSpec === 'auto') {
477
- // Auto-precision - base on scale of number
478
- if (absVal === 0) {
479
- precision = 2;
480
- } else if (absVal < 0.01) {
481
- precision = 6;
482
- } else if (absVal < 100) {
483
- precision = 4;
484
- } else if (absVal < 10000) {
485
- precision = 2;
486
- } else {
487
- precision = 0;
488
- }
489
- } else {
490
- // Fixed precision - use requested, capped at max.
491
- precision = precisionSpec < MAX_NUMERIC_PRECISION ? precisionSpec : MAX_NUMERIC_PRECISION;
492
- }
529
+ const precision = parsePrecision(v, precisionSpec);
493
530
 
494
531
  // If zeroPad gte precision, treat as `true` to pad out to (but not beyond) full precision.
495
532
  // We don't support applying some precision (rounding) then padding out zeroes after that.
package/icon/Icon.scss ADDED
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This file belongs to Hoist, an application development toolkit
3
+ * developed by Extremely Heavy Industries (www.xh.io | info@xh.io)
4
+ *
5
+ * Copyright © 2026 Extremely Heavy Industries Inc.
6
+ */
7
+
8
+ // FA v7 sizes icons via the `--fa-width` custom property (default 1.25em) rather than the old
9
+ // `fa-fw` class. The placeholder is a plain div, so it reads the same property to stay aligned
10
+ // with sibling icons under any width override, falling back to FA's default when unset.
11
+ .xh-icon--placeholder {
12
+ width: var(--fa-width, 1.25em);
13
+ }
package/icon/Icon.ts CHANGED
@@ -15,6 +15,7 @@ import {ReactElement} from 'react';
15
15
  import {iconCmp} from './impl/IconCmp';
16
16
  import {enhanceFaClasses, iconHtml} from './impl/IconHtml';
17
17
  import {SetRequired} from 'type-fest';
18
+ import './Icon.scss';
18
19
 
19
20
  export interface IconProps extends HoistProps, Partial<Omit<FontAwesomeIconProps, 'ref'>> {
20
21
  /** Name of the icon in FontAwesome. */
@@ -932,9 +933,9 @@ export const Icon = {
932
933
  },
933
934
 
934
935
  /**
935
- * Returns an empty div with FA sizing classes applied. Can be used to take up room in a layout
936
- * where an icon might otherwise go - e.g. to align a series of menu items, where some items do
937
- * not have an icon but others do.
936
+ * Returns an empty div sized to occupy the width of a standard icon. Can be used to take up
937
+ * room in a layout where an icon might otherwise go - e.g. to align a series of menu items,
938
+ * where some items do not have an icon but others do.
938
939
  */
939
940
  placeholder(opts?: IconProps): any {
940
941
  const {size, asHtml = false} = opts ?? {},
@@ -32,5 +32,5 @@ export function iconHtml({
32
32
  }
33
33
 
34
34
  export function enhanceFaClasses(className: string, size: string) {
35
- return classNames(className, 'fa-fw', 'xh-icon', isString(size) ? `fa-${size}` : null);
35
+ return classNames(className, 'xh-icon', isString(size) ? `fa-${size}` : null);
36
36
  }
@@ -1432,7 +1432,7 @@ function extractSymbolDetail(entry: SymbolEntry): SymbolDetail | null {
1432
1432
  *
1433
1433
  * `JSDoc.getDescription()` returns the text before the first JSDoc block tag,
1434
1434
  * but the TS parser treats ANY line whose first non-whitespace char is `@` as a
1435
- * tag boundary - even inside a fenced ``` code block. So a canonical-usage
1435
+ * tag boundary - even inside a fenced code block. So a canonical-usage
1436
1436
  * example containing e.g. `@observable.ref` silently truncates the description
1437
1437
  * mid-example, dropping everything after it (further examples, "SEE ALSO"
1438
1438
  * lists, trailing prose). See #4352.
@@ -21,7 +21,8 @@ export interface ExpandToLevelButtonProps extends MenuButtonProps {
21
21
 
22
22
  /**
23
23
  * A menu button to expand a multi-level grouped or tree grid out to a desired level.
24
- * Requires {@link GridConfig.levelLabels} to be configured on the bound GridModel.
24
+ * Requires {@link GridConfig.levelLabels} to be configured on the bound GridModel - the menu offers
25
+ * one entry per labelled level, so a partial array will limit the available expand-to targets.
25
26
  */
26
27
  export const [ExpandToLevelButton, expandToLevelButton] =
27
28
  hoistCmp.withFactory<ExpandToLevelButtonProps>({
@@ -51,13 +52,11 @@ export const [ExpandToLevelButton, expandToLevelButton] =
51
52
  }
52
53
 
53
54
  // Render a disabled button if requested or if we have a flat grid, or no level labels
54
- const {maxDepth, expandLevel, resolvedLevelLabels} = gridModel;
55
+ const {maxDepth, resolvedLevelLabels} = gridModel;
55
56
  if (disabled || !maxDepth || !resolvedLevelLabels) return disabledButton();
56
57
 
57
58
  const menuItems: MenuItemLike[] = resolvedLevelLabels.map((label, idx) => {
58
- const isCurrLevel =
59
- expandLevel === idx ||
60
- (expandLevel > maxDepth && idx === resolvedLevelLabels.length - 1);
59
+ const isCurrLevel = gridModel.isCurrentExpandLevel(idx);
61
60
 
62
61
  return {
63
62
  icon: isCurrLevel ? Icon.check() : Icon.placeholder(),
@@ -44,7 +44,24 @@ class CheckboxButtonInputModel extends HoistInputModel {
44
44
  // Implementation
45
45
  //----------------------------------
46
46
  const cmp = hoistCmp.factory<CheckboxButtonInputModel>(
47
- ({model, text, checkedIcon, uncheckedIcon, ...props}, ref) => {
47
+ (
48
+ {
49
+ // HoistInput props - exclude from passthrough to Onsen
50
+ bind,
51
+ value,
52
+ commitOnChange,
53
+ onChange,
54
+ onCommit,
55
+ // Consumed by this component
56
+ model,
57
+ text,
58
+ checkedIcon,
59
+ uncheckedIcon,
60
+ // Remainder passed to hoist button & Onsen button
61
+ ...props
62
+ },
63
+ ref
64
+ ) => {
48
65
  const checked = !!model.renderValue;
49
66
  return button({
50
67
  text: withDefault(text, model.getField()?.displayName),
@@ -46,7 +46,7 @@ export interface NumberInputProps extends HoistProps, HoistInputProps, StyleProp
46
46
  /** Text to display when control is empty. */
47
47
  placeholder?: string;
48
48
 
49
- /** Max decimal precision of the value, defaults to 4. */
49
+ /** Max decimal precision of the value, defaults to 4. Set to null for full, unrestricted precision. */
50
50
  precision?: NumericPrecision;
51
51
 
52
52
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "86.1.0",
3
+ "version": "86.2.0",
4
4
  "description": "Hoist add-on for building and deploying React Applications.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,11 +42,11 @@
42
42
  "@azure/msal-browser": "~5.14.0",
43
43
  "@blueprintjs/core": "^6.3.2",
44
44
  "@blueprintjs/datetime": "^6.0.6",
45
- "@codemirror/commands": "~6.10.3",
46
- "@codemirror/language": "~6.12.3",
47
- "@codemirror/language-data": "~6.5.2",
48
- "@codemirror/lint": "~6.9.6",
49
- "@codemirror/state": "~6.6.0",
45
+ "@codemirror/commands": "^6.10.3",
46
+ "@codemirror/language": "^6.12.3",
47
+ "@codemirror/language-data": "^6.5.2",
48
+ "@codemirror/lint": "^6.9.6",
49
+ "@codemirror/state": "^6.7.0",
50
50
  "@codemirror/view": "^6.43.0",
51
51
  "@fortawesome/fontawesome-pro": "^7.2.0",
52
52
  "@fortawesome/fontawesome-svg-core": "^7.2.0",
package/styles/vars.scss CHANGED
@@ -844,6 +844,21 @@ body {
844
844
  --xh-tab-font-size: var(--tab-font-size, var(--xh-font-size));
845
845
  --xh-tab-font-size-px: calc(var(--xh-tab-font-size) * 1px);
846
846
 
847
+ // Vertical (left/right) TabSwitcher - the styled "rail" of rounded, hoverable items with a
848
+ // filled active "pill". All overridable via the unprefixed hooks.
849
+ --xh-tab-switcher-vertical-min-width: var(--tab-switcher-vertical-min-width, auto);
850
+ --xh-tab-switcher-vertical-padding: var(--tab-switcher-vertical-padding, var(--xh-pad-half-px));
851
+ --xh-tab-switcher-vertical-border: var(--tab-switcher-vertical-border, var(--xh-border-solid));
852
+ --xh-tab-switcher-vertical-item-gap: var(--tab-switcher-vertical-item-gap, 2px);
853
+ --xh-tab-switcher-vertical-item-padding: var(--tab-switcher-vertical-item-padding, var(--xh-pad-half-px) var(--xh-pad-px));
854
+ --xh-tab-switcher-vertical-item-border-radius: var(--tab-switcher-vertical-item-border-radius, var(--xh-border-radius-px));
855
+ --xh-tab-switcher-vertical-item-text-color: var(--tab-switcher-vertical-item-text-color, var(--xh-text-color-muted));
856
+ --xh-tab-switcher-vertical-item-active-text-color: var(--tab-switcher-vertical-item-active-text-color, var(--xh-tab-active-text-color));
857
+ --xh-tab-switcher-vertical-item-hover-bg: var(--tab-switcher-vertical-item-hover-bg, var(--xh-bg-alt));
858
+ --xh-tab-switcher-vertical-item-active-bg: var(--tab-switcher-vertical-item-active-bg, var(--xh-bg-highlight));
859
+ --xh-tab-switcher-vertical-item-font-weight: var(--tab-switcher-vertical-item-font-weight, 500);
860
+ --xh-tab-switcher-vertical-item-active-font-weight: var(--tab-switcher-vertical-item-active-font-weight, 600);
861
+
847
862
  &.xh-dark {
848
863
  --xh-tab-active-text-color: var(--tab-active-text-color, var(--xh-intent-primary-lighter));
849
864
  }