@vizejs/fresco 0.720.0 → 0.722.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.
@@ -1,6 +1,6 @@
1
1
  import { A as useFocusManager, O as provideFocusScope, k as useFocus, m as getLastRenderLayout } from "./app-CjfLCWic.mjs";
2
2
  import { a as useInput, c as useIsScreenReaderEnabled, i as useMouse, n as createVirtualViewport, t as useBoxMetrics } from "./useBoxMetrics-BMD1acRU.mjs";
3
- import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, h, mergeProps, nextTick, normalizeStyle, onMounted, onUnmounted, openBlock, ref, renderList, renderSlot, resolveDynamicComponent, shallowRef, toDisplayString, unref, useSlots, useTemplateRef, watch, withCtx } from "vue";
3
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, h, mergeProps, nextTick, normalizeStyle, onMounted, onUnmounted, openBlock, ref, renderList, renderSlot, resolveDynamicComponent, shallowRef, toDisplayString, toRaw, unref, useSlots, useTemplateRef, watch, withCtx } from "vue";
4
4
  //#region src/components/Box.ts
5
5
  /**
6
6
  * Box Component - Container with flexbox layout
@@ -3339,10 +3339,23 @@ function visibleCollectionOffset(highlightedIndex, currentOffset, windowSize, it
3339
3339
  return Math.min(currentOffset, maximumOffset);
3340
3340
  }
3341
3341
  //#endregion
3342
+ //#region src/components/collection-values.ts
3343
+ /** Compare collection values by identity, an object key, or a custom comparator. */
3344
+ function collectionValuesEqual(left, right, by) {
3345
+ if (typeof by === "function") return by(left, right);
3346
+ if (by !== void 0 && typeof left === "object" && left !== null && typeof right === "object" && right !== null) return Object.is(Reflect.get(left, by), Reflect.get(right, by));
3347
+ return Object.is(left, right);
3348
+ }
3349
+ //#endregion
3342
3350
  //#region src/components/Select.ts
3343
3351
  /**
3344
3352
  * Select Component - Dropdown/menu selection
3345
3353
  */
3354
+ function optionKey(option, index) {
3355
+ if (option.key !== void 0) return option.key;
3356
+ const value = option.value;
3357
+ return typeof value === "string" || typeof value === "number" || typeof value === "symbol" ? value : index;
3358
+ }
3346
3359
  const Select = defineComponent({
3347
3360
  name: "Select",
3348
3361
  props: {
@@ -3350,7 +3363,13 @@ const Select = defineComponent({
3350
3363
  type: Array,
3351
3364
  required: true
3352
3365
  },
3353
- modelValue: String,
3366
+ modelValue: null,
3367
+ by: [
3368
+ String,
3369
+ Number,
3370
+ Symbol,
3371
+ Function
3372
+ ],
3354
3373
  placeholder: {
3355
3374
  type: String,
3356
3375
  default: "Select an option"
@@ -3385,8 +3404,8 @@ const Select = defineComponent({
3385
3404
  if (props.maxHeight === void 0 || !Number.isFinite(props.maxHeight)) return void 0;
3386
3405
  return props.maxHeight > 0 ? Math.max(1, Math.floor(props.maxHeight)) : void 0;
3387
3406
  });
3388
- watch(() => props.modelValue, (value) => {
3389
- const selectedIndex = props.options.findIndex((option) => option.value === value && !option.disabled);
3407
+ watch([() => props.modelValue, () => props.by], (value) => {
3408
+ const selectedIndex = props.options.findIndex((option) => collectionValuesEqual(option.value, value[0], value[1]) && !option.disabled);
3390
3409
  highlightedIndex.value = selectedIndex >= 0 ? selectedIndex : firstEnabledIndex(props.options);
3391
3410
  }, { immediate: true });
3392
3411
  watch(() => props.options, (options) => {
@@ -3402,8 +3421,9 @@ const Select = defineComponent({
3402
3421
  const activate = () => {
3403
3422
  const option = props.options[highlightedIndex.value];
3404
3423
  if (!option || option.disabled) return;
3405
- emit("update:modelValue", option.value);
3406
- emit("select", option.value);
3424
+ const value = toRaw(option.value);
3425
+ emit("update:modelValue", value);
3426
+ emit("select", value);
3407
3427
  };
3408
3428
  useInput({
3409
3429
  isActive: computed(() => props.focused),
@@ -3448,10 +3468,10 @@ const Select = defineComponent({
3448
3468
  }, " ..."));
3449
3469
  for (const { option, index } of visibleOptions.value) {
3450
3470
  const isHighlighted = index === highlightedIndex.value;
3451
- const isSelected = option.value === props.modelValue;
3471
+ const isSelected = collectionValuesEqual(option.value, props.modelValue, props.by);
3452
3472
  const indicator = isHighlighted ? props.indicator : props.indicatorEmpty;
3453
3473
  children.push(h("box", {
3454
- key: option.value,
3474
+ key: optionKey(option, index),
3455
3475
  style: { flex_direction: "row" }
3456
3476
  }, [h("text", {
3457
3477
  fg: isHighlighted ? props.selectedFg : props.fg,
@@ -3472,6 +3492,95 @@ const Select = defineComponent({
3472
3492
  }
3473
3493
  });
3474
3494
  //#endregion
3495
+ //#region src/components/FrescoSelect.vue
3496
+ var FrescoSelect_default = /* @__PURE__ */ defineComponent({
3497
+ name: "FrescoSelect",
3498
+ __name: "FrescoSelect",
3499
+ props: {
3500
+ options: {
3501
+ type: Array,
3502
+ required: true
3503
+ },
3504
+ modelValue: {
3505
+ type: null,
3506
+ required: false
3507
+ },
3508
+ by: {
3509
+ type: Function,
3510
+ required: false,
3511
+ skipCheck: true
3512
+ },
3513
+ placeholder: {
3514
+ type: String,
3515
+ required: false
3516
+ },
3517
+ maxHeight: {
3518
+ type: Number,
3519
+ required: false
3520
+ },
3521
+ focused: {
3522
+ type: Boolean,
3523
+ required: false
3524
+ },
3525
+ indicator: {
3526
+ type: String,
3527
+ required: false
3528
+ },
3529
+ indicatorEmpty: {
3530
+ type: String,
3531
+ required: false
3532
+ },
3533
+ fg: {
3534
+ type: String,
3535
+ required: false
3536
+ },
3537
+ bg: {
3538
+ type: String,
3539
+ required: false
3540
+ },
3541
+ selectedFg: {
3542
+ type: String,
3543
+ required: false
3544
+ },
3545
+ selectedBg: {
3546
+ type: String,
3547
+ required: false
3548
+ }
3549
+ },
3550
+ emits: ["update:modelValue", "select"],
3551
+ setup(__props, { emit: __emit }) {
3552
+ const emit = __emit;
3553
+ const runtimeProps = computed(() => {
3554
+ return {
3555
+ options: __props.options,
3556
+ modelValue: __props.modelValue,
3557
+ by: typeof __props.by === "function" ? (left, right) => __props.by(left, right) : __props.by,
3558
+ placeholder: __props.placeholder,
3559
+ maxHeight: __props.maxHeight,
3560
+ focused: __props.focused,
3561
+ indicator: __props.indicator,
3562
+ indicatorEmpty: __props.indicatorEmpty,
3563
+ fg: __props.fg,
3564
+ bg: __props.bg,
3565
+ selectedFg: __props.selectedFg,
3566
+ selectedBg: __props.selectedBg
3567
+ };
3568
+ });
3569
+ function updateModelValue(value) {
3570
+ emit("update:modelValue", value);
3571
+ }
3572
+ function selectValue(value) {
3573
+ emit("select", value);
3574
+ }
3575
+ return (_ctx, _cache) => {
3576
+ return openBlock(), createBlock(unref(Select), mergeProps(runtimeProps.value, {
3577
+ "onUpdate:modelValue": updateModelValue,
3578
+ onSelect: selectValue
3579
+ }), null, 16);
3580
+ };
3581
+ }
3582
+ });
3583
+ //#endregion
3475
3584
  //#region src/components/Checkbox.ts
3476
3585
  /**
3477
3586
  * Checkbox Component - Toggle checkbox
@@ -4805,6 +4914,9 @@ var FrescoTooltip_default = /* @__PURE__ */ defineComponent({
4805
4914
  /**
4806
4915
  * List Component - Scrollable list of items
4807
4916
  */
4917
+ function listItemValue(item) {
4918
+ return Object.hasOwn(item, "value") ? item.value : item.key;
4919
+ }
4808
4920
  const List = defineComponent({
4809
4921
  name: "List",
4810
4922
  props: {
@@ -4812,7 +4924,13 @@ const List = defineComponent({
4812
4924
  type: Array,
4813
4925
  required: true
4814
4926
  },
4815
- modelValue: String,
4927
+ modelValue: null,
4928
+ by: [
4929
+ String,
4930
+ Number,
4931
+ Symbol,
4932
+ Function
4933
+ ],
4816
4934
  maxHeight: Number,
4817
4935
  focused: {
4818
4936
  type: Boolean,
@@ -4839,11 +4957,15 @@ const List = defineComponent({
4839
4957
  const highlightedIndex = ref(-1);
4840
4958
  const typeahead = createTypeaheadBuffer();
4841
4959
  const syncHighlight = () => {
4842
- const selectedIndex = props.items.findIndex((item) => item.key === props.modelValue && !item.disabled);
4960
+ const selectedIndex = props.items.findIndex((item) => collectionValuesEqual(listItemValue(item), props.modelValue, props.by) && !item.disabled);
4843
4961
  if (selectedIndex >= 0) highlightedIndex.value = selectedIndex;
4844
4962
  else if (highlightedIndex.value < 0 || highlightedIndex.value >= props.items.length || props.items[highlightedIndex.value]?.disabled) highlightedIndex.value = firstEnabledIndex(props.items);
4845
4963
  };
4846
- watch([() => props.modelValue, () => props.items], syncHighlight, {
4964
+ watch([
4965
+ () => props.modelValue,
4966
+ () => props.items,
4967
+ () => props.by
4968
+ ], syncHighlight, {
4847
4969
  deep: true,
4848
4970
  immediate: true
4849
4971
  });
@@ -4867,8 +4989,9 @@ const List = defineComponent({
4867
4989
  const activate = () => {
4868
4990
  const item = props.items[highlightedIndex.value];
4869
4991
  if (!item || item.disabled) return;
4870
- emit("update:modelValue", item.key);
4871
- emit("select", item.key);
4992
+ const value = toRaw(listItemValue(item));
4993
+ emit("update:modelValue", value);
4994
+ emit("select", value);
4872
4995
  };
4873
4996
  useInput({
4874
4997
  isActive: computed(() => props.focused),
@@ -4911,7 +5034,7 @@ const List = defineComponent({
4911
5034
  }, " ..."));
4912
5035
  visibleItems.value.forEach((item, visibleIndex) => {
4913
5036
  const isHighlighted = scrollOffset.value + visibleIndex === highlightedIndex.value;
4914
- const isSelected = item.key === props.modelValue;
5037
+ const isSelected = collectionValuesEqual(listItemValue(item), props.modelValue, props.by);
4915
5038
  const indicator = isHighlighted ? props.indicator : props.indicatorEmpty;
4916
5039
  children.push(h("text", {
4917
5040
  key: item.key,
@@ -4933,6 +5056,85 @@ const List = defineComponent({
4933
5056
  }
4934
5057
  });
4935
5058
  //#endregion
5059
+ //#region src/components/FrescoList.vue
5060
+ var FrescoList_default = /* @__PURE__ */ defineComponent({
5061
+ name: "FrescoList",
5062
+ __name: "FrescoList",
5063
+ props: {
5064
+ items: {
5065
+ type: Array,
5066
+ required: true
5067
+ },
5068
+ modelValue: {
5069
+ type: null,
5070
+ required: false
5071
+ },
5072
+ by: {
5073
+ type: Function,
5074
+ required: false,
5075
+ skipCheck: true
5076
+ },
5077
+ maxHeight: {
5078
+ type: Number,
5079
+ required: false
5080
+ },
5081
+ focused: {
5082
+ type: Boolean,
5083
+ required: false
5084
+ },
5085
+ indicator: {
5086
+ type: String,
5087
+ required: false
5088
+ },
5089
+ indicatorEmpty: {
5090
+ type: String,
5091
+ required: false
5092
+ },
5093
+ fg: {
5094
+ type: String,
5095
+ required: false
5096
+ },
5097
+ selectedFg: {
5098
+ type: String,
5099
+ required: false
5100
+ },
5101
+ selectedBg: {
5102
+ type: String,
5103
+ required: false
5104
+ }
5105
+ },
5106
+ emits: ["update:modelValue", "select"],
5107
+ setup(__props, { emit: __emit }) {
5108
+ const emit = __emit;
5109
+ const runtimeProps = computed(() => {
5110
+ return {
5111
+ items: __props.items,
5112
+ modelValue: __props.modelValue,
5113
+ by: typeof __props.by === "function" ? (left, right) => __props.by(left, right) : __props.by,
5114
+ maxHeight: __props.maxHeight,
5115
+ focused: __props.focused,
5116
+ indicator: __props.indicator,
5117
+ indicatorEmpty: __props.indicatorEmpty,
5118
+ fg: __props.fg,
5119
+ selectedFg: __props.selectedFg,
5120
+ selectedBg: __props.selectedBg
5121
+ };
5122
+ });
5123
+ function updateModelValue(value) {
5124
+ emit("update:modelValue", value);
5125
+ }
5126
+ function selectValue(value) {
5127
+ emit("select", value);
5128
+ }
5129
+ return (_ctx, _cache) => {
5130
+ return openBlock(), createBlock(unref(List), mergeProps(runtimeProps.value, {
5131
+ "onUpdate:modelValue": updateModelValue,
5132
+ onSelect: selectValue
5133
+ }), null, 16);
5134
+ };
5135
+ }
5136
+ });
5137
+ //#endregion
4936
5138
  //#region src/components/table-utils.ts
4937
5139
  function finiteInteger(value, fallback) {
4938
5140
  return Number.isFinite(value) ? Math.trunc(value) : fallback;
@@ -5209,6 +5411,9 @@ const Table = defineComponent({
5209
5411
  /**
5210
5412
  * Tree Component - Tree view for hierarchical data
5211
5413
  */
5414
+ function treeNodeValue(node) {
5415
+ return Object.hasOwn(node, "value") ? node.value : node.key;
5416
+ }
5212
5417
  const Tree = defineComponent({
5213
5418
  name: "Tree",
5214
5419
  props: {
@@ -5220,11 +5425,18 @@ const Tree = defineComponent({
5220
5425
  type: Array,
5221
5426
  default: () => []
5222
5427
  },
5223
- selected: String,
5428
+ selected: null,
5429
+ by: [
5430
+ String,
5431
+ Number,
5432
+ Symbol,
5433
+ Function
5434
+ ],
5224
5435
  focused: {
5225
5436
  type: Boolean,
5226
5437
  default: false
5227
5438
  },
5439
+ maxHeight: Number,
5228
5440
  showLines: {
5229
5441
  type: Boolean,
5230
5442
  default: true
@@ -5283,22 +5495,34 @@ const Tree = defineComponent({
5283
5495
  return visible;
5284
5496
  });
5285
5497
  const highlightedKey = ref();
5498
+ const scrollOffset = ref(0);
5286
5499
  const typeahead = createTypeaheadBuffer();
5500
+ const viewportSize = computed(() => {
5501
+ if (props.maxHeight === void 0 || !Number.isFinite(props.maxHeight)) return void 0;
5502
+ return props.maxHeight > 0 ? Math.max(1, Math.floor(props.maxHeight)) : void 0;
5503
+ });
5287
5504
  const navigableNodes = computed(() => visibleNodes.value.map(({ node }) => ({ disabled: node.disabled })));
5288
5505
  const highlightedIndex = computed(() => visibleNodes.value.findIndex(({ node }) => node.key === highlightedKey.value));
5289
5506
  const syncVisibleHighlight = () => {
5290
5507
  const currentIndex = highlightedIndex.value;
5291
5508
  if (currentIndex >= 0 && !visibleNodes.value[currentIndex]?.node.disabled) return;
5292
- const selectedIndex = visibleNodes.value.findIndex(({ node }) => node.key === props.selected && !node.disabled);
5509
+ const selectedIndex = visibleNodes.value.findIndex(({ node }) => collectionValuesEqual(treeNodeValue(node), props.selected, props.by) && !node.disabled);
5293
5510
  const nextIndex = selectedIndex >= 0 ? selectedIndex : firstEnabledIndex(navigableNodes.value);
5294
5511
  highlightedKey.value = visibleNodes.value[nextIndex]?.node.key;
5295
5512
  };
5296
- watch(() => props.selected, (selected) => {
5297
- const selectedIndex = visibleNodes.value.findIndex(({ node }) => node.key === selected && !node.disabled);
5513
+ watch([() => props.selected, () => props.by], ([selected, by]) => {
5514
+ const selectedIndex = visibleNodes.value.findIndex(({ node }) => collectionValuesEqual(treeNodeValue(node), selected, by) && !node.disabled);
5298
5515
  if (selectedIndex >= 0) highlightedKey.value = visibleNodes.value[selectedIndex]?.node.key;
5299
5516
  else syncVisibleHighlight();
5300
5517
  }, { immediate: true });
5301
5518
  watch(visibleNodes, syncVisibleHighlight, { deep: true });
5519
+ watch([
5520
+ highlightedIndex,
5521
+ viewportSize,
5522
+ () => visibleNodes.value.length
5523
+ ], ([index, windowSize]) => {
5524
+ scrollOffset.value = visibleCollectionOffset(index, scrollOffset.value, windowSize, visibleNodes.value.length);
5525
+ }, { immediate: true });
5302
5526
  const setHighlight = (index) => {
5303
5527
  const entry = visibleNodes.value[index];
5304
5528
  if (!entry || entry.node.disabled) return;
@@ -5314,8 +5538,9 @@ const Tree = defineComponent({
5314
5538
  const activate = () => {
5315
5539
  const entry = visibleNodes.value[highlightedIndex.value];
5316
5540
  if (!entry || entry.node.disabled) return;
5317
- emit("update:selected", entry.node.key);
5318
- emit("select", entry.node.key);
5541
+ const value = toRaw(treeNodeValue(entry.node));
5542
+ emit("update:selected", value);
5543
+ emit("select", value);
5319
5544
  };
5320
5545
  const moveToParent = (parentKey) => {
5321
5546
  let key = parentKey;
@@ -5359,17 +5584,153 @@ const Tree = defineComponent({
5359
5584
  },
5360
5585
  onSubmit: activate
5361
5586
  });
5362
- return () => h("box", { style: { flex_direction: "column" } }, visibleNodes.value.map(({ node, prefix, icon }) => {
5363
- const isSelected = node.key === props.selected;
5364
- const isHighlighted = node.key === highlightedKey.value;
5365
- return h("text", {
5366
- key: node.key,
5367
- fg: isSelected || isHighlighted ? props.selectedFg : props.fg,
5368
- bold: isSelected,
5369
- underline: props.focused && isHighlighted,
5370
- dim: node.disabled
5371
- }, `${prefix}${icon} ${node.label}`);
5372
- }));
5587
+ const viewportNodes = computed(() => {
5588
+ const windowSize = viewportSize.value;
5589
+ const start = windowSize === void 0 ? 0 : scrollOffset.value;
5590
+ const end = windowSize === void 0 ? visibleNodes.value.length : start + windowSize;
5591
+ return visibleNodes.value.slice(start, end);
5592
+ });
5593
+ const scrollIndicator = computed(() => {
5594
+ const windowSize = viewportSize.value;
5595
+ if (windowSize === void 0 || visibleNodes.value.length <= windowSize) return {
5596
+ showUp: false,
5597
+ showDown: false
5598
+ };
5599
+ return {
5600
+ showUp: scrollOffset.value > 0,
5601
+ showDown: scrollOffset.value + windowSize < visibleNodes.value.length
5602
+ };
5603
+ });
5604
+ return () => {
5605
+ const children = [];
5606
+ if (scrollIndicator.value.showUp) children.push(h("text", {
5607
+ key: "scroll-up",
5608
+ dim: true
5609
+ }, " ..."));
5610
+ for (const { node, prefix, icon } of viewportNodes.value) {
5611
+ const isSelected = collectionValuesEqual(treeNodeValue(node), props.selected, props.by);
5612
+ const isHighlighted = node.key === highlightedKey.value;
5613
+ children.push(h("text", {
5614
+ key: node.key,
5615
+ fg: isSelected || isHighlighted ? props.selectedFg : props.fg,
5616
+ bold: isSelected,
5617
+ underline: props.focused && isHighlighted,
5618
+ dim: node.disabled
5619
+ }, `${prefix}${icon} ${node.label}`));
5620
+ }
5621
+ if (scrollIndicator.value.showDown) children.push(h("text", {
5622
+ key: "scroll-down",
5623
+ dim: true
5624
+ }, " ..."));
5625
+ return h("box", { style: { flex_direction: "column" } }, children);
5626
+ };
5627
+ }
5628
+ });
5629
+ //#endregion
5630
+ //#region src/components/FrescoTree.vue
5631
+ var FrescoTree_default = /* @__PURE__ */ defineComponent({
5632
+ name: "FrescoTree",
5633
+ __name: "FrescoTree",
5634
+ props: {
5635
+ data: {
5636
+ type: Array,
5637
+ required: true
5638
+ },
5639
+ expanded: {
5640
+ type: Array,
5641
+ required: false
5642
+ },
5643
+ selected: {
5644
+ type: null,
5645
+ required: false
5646
+ },
5647
+ by: {
5648
+ type: Function,
5649
+ required: false,
5650
+ skipCheck: true
5651
+ },
5652
+ focused: {
5653
+ type: Boolean,
5654
+ required: false
5655
+ },
5656
+ maxHeight: {
5657
+ type: Number,
5658
+ required: false
5659
+ },
5660
+ showLines: {
5661
+ type: Boolean,
5662
+ required: false
5663
+ },
5664
+ indent: {
5665
+ type: Number,
5666
+ required: false
5667
+ },
5668
+ expandedIcon: {
5669
+ type: String,
5670
+ required: false
5671
+ },
5672
+ collapsedIcon: {
5673
+ type: String,
5674
+ required: false
5675
+ },
5676
+ leafIcon: {
5677
+ type: String,
5678
+ required: false
5679
+ },
5680
+ fg: {
5681
+ type: String,
5682
+ required: false
5683
+ },
5684
+ selectedFg: {
5685
+ type: String,
5686
+ required: false
5687
+ }
5688
+ },
5689
+ emits: [
5690
+ "select",
5691
+ "update:selected",
5692
+ "toggle",
5693
+ "update:expanded"
5694
+ ],
5695
+ setup(__props, { emit: __emit }) {
5696
+ const emit = __emit;
5697
+ const runtimeProps = computed(() => {
5698
+ return {
5699
+ data: __props.data,
5700
+ expanded: __props.expanded,
5701
+ selected: __props.selected,
5702
+ by: typeof __props.by === "function" ? (left, right) => __props.by(left, right) : __props.by,
5703
+ focused: __props.focused,
5704
+ maxHeight: __props.maxHeight,
5705
+ showLines: __props.showLines,
5706
+ indent: __props.indent,
5707
+ expandedIcon: __props.expandedIcon,
5708
+ collapsedIcon: __props.collapsedIcon,
5709
+ leafIcon: __props.leafIcon,
5710
+ fg: __props.fg,
5711
+ selectedFg: __props.selectedFg
5712
+ };
5713
+ });
5714
+ function selectValue(value) {
5715
+ emit("select", value);
5716
+ }
5717
+ function updateSelected(value) {
5718
+ emit("update:selected", value);
5719
+ }
5720
+ function toggle(key, expanded) {
5721
+ emit("toggle", key, expanded);
5722
+ }
5723
+ function updateExpanded(keys) {
5724
+ emit("update:expanded", keys);
5725
+ }
5726
+ return (_ctx, _cache) => {
5727
+ return openBlock(), createBlock(unref(Tree), mergeProps(runtimeProps.value, {
5728
+ onSelect: selectValue,
5729
+ "onUpdate:selected": updateSelected,
5730
+ onToggle: toggle,
5731
+ "onUpdate:expanded": updateExpanded
5732
+ }), null, 16);
5733
+ };
5373
5734
  }
5374
5735
  });
5375
5736
  //#endregion
@@ -5377,6 +5738,9 @@ const Tree = defineComponent({
5377
5738
  /**
5378
5739
  * Menu Component - Command menu/palette
5379
5740
  */
5741
+ function menuItemValue(item) {
5742
+ return Object.hasOwn(item, "value") ? item.value : item.key;
5743
+ }
5380
5744
  const Menu = defineComponent({
5381
5745
  name: "Menu",
5382
5746
  props: {
@@ -5392,6 +5756,7 @@ const Menu = defineComponent({
5392
5756
  type: Boolean,
5393
5757
  default: false
5394
5758
  },
5759
+ maxHeight: Number,
5395
5760
  border: {
5396
5761
  type: String,
5397
5762
  default: "single"
@@ -5413,8 +5778,13 @@ const Menu = defineComponent({
5413
5778
  },
5414
5779
  emits: ["select", "update:focusedIndex"],
5415
5780
  setup(props, { emit }) {
5781
+ const scrollOffset = ref(0);
5416
5782
  const highlightedIndex = ref(-1);
5417
5783
  const typeahead = createTypeaheadBuffer();
5784
+ const viewportSize = computed(() => {
5785
+ if (props.maxHeight === void 0 || !Number.isFinite(props.maxHeight)) return void 0;
5786
+ return props.maxHeight > 0 ? Math.max(1, Math.floor(props.maxHeight)) : void 0;
5787
+ });
5418
5788
  const syncHighlight = () => {
5419
5789
  const requested = props.focusedIndex;
5420
5790
  const requestedItem = props.items[requested];
@@ -5424,6 +5794,13 @@ const Menu = defineComponent({
5424
5794
  deep: true,
5425
5795
  immediate: true
5426
5796
  });
5797
+ watch([
5798
+ highlightedIndex,
5799
+ viewportSize,
5800
+ () => props.items.length
5801
+ ], ([index, windowSize]) => {
5802
+ scrollOffset.value = visibleCollectionOffset(index, scrollOffset.value, windowSize, props.items.length);
5803
+ }, { immediate: true });
5427
5804
  const setHighlight = (index) => {
5428
5805
  if (index < 0 || index === highlightedIndex.value) return;
5429
5806
  highlightedIndex.value = index;
@@ -5432,7 +5809,7 @@ const Menu = defineComponent({
5432
5809
  const activate = () => {
5433
5810
  const item = props.items[highlightedIndex.value];
5434
5811
  if (!item || item.disabled || item.separator) return;
5435
- emit("select", item.key);
5812
+ emit("select", toRaw(menuItemValue(item)));
5436
5813
  };
5437
5814
  useInput({
5438
5815
  isActive: computed(() => props.focused),
@@ -5453,9 +5830,33 @@ const Menu = defineComponent({
5453
5830
  },
5454
5831
  onSubmit: activate
5455
5832
  });
5833
+ const visibleItems = computed(() => {
5834
+ const windowSize = viewportSize.value;
5835
+ const start = windowSize === void 0 ? 0 : scrollOffset.value;
5836
+ const end = windowSize === void 0 ? props.items.length : start + windowSize;
5837
+ return props.items.slice(start, end).map((item, visibleIndex) => ({
5838
+ item,
5839
+ index: start + visibleIndex
5840
+ }));
5841
+ });
5842
+ const scrollIndicator = computed(() => {
5843
+ const windowSize = viewportSize.value;
5844
+ if (windowSize === void 0 || props.items.length <= windowSize) return {
5845
+ showUp: false,
5846
+ showDown: false
5847
+ };
5848
+ return {
5849
+ showUp: scrollOffset.value > 0,
5850
+ showDown: scrollOffset.value + windowSize < props.items.length
5851
+ };
5852
+ });
5456
5853
  return () => {
5457
5854
  const children = [];
5458
- props.items.forEach((item, index) => {
5855
+ if (scrollIndicator.value.showUp) children.push(h("text", {
5856
+ key: "scroll-up",
5857
+ dim: true
5858
+ }, " ..."));
5859
+ visibleItems.value.forEach(({ item, index }) => {
5459
5860
  if (item.separator) {
5460
5861
  children.push(h("text", {
5461
5862
  key: `sep-${index}`,
@@ -5488,6 +5889,10 @@ const Menu = defineComponent({
5488
5889
  bg: isFocused ? props.focusedBg : void 0
5489
5890
  }, itemContent));
5490
5891
  });
5892
+ if (scrollIndicator.value.showDown) children.push(h("text", {
5893
+ key: "scroll-down",
5894
+ dim: true
5895
+ }, " ..."));
5491
5896
  return h("box", {
5492
5897
  border: props.border === "none" ? void 0 : props.border,
5493
5898
  style: {
@@ -5499,10 +5904,89 @@ const Menu = defineComponent({
5499
5904
  }
5500
5905
  });
5501
5906
  //#endregion
5907
+ //#region src/components/FrescoMenu.vue
5908
+ var FrescoMenu_default = /* @__PURE__ */ defineComponent({
5909
+ name: "FrescoMenu",
5910
+ __name: "FrescoMenu",
5911
+ props: {
5912
+ items: {
5913
+ type: Array,
5914
+ required: true
5915
+ },
5916
+ focusedIndex: {
5917
+ type: Number,
5918
+ required: false
5919
+ },
5920
+ focused: {
5921
+ type: Boolean,
5922
+ required: false
5923
+ },
5924
+ maxHeight: {
5925
+ type: Number,
5926
+ required: false
5927
+ },
5928
+ border: {
5929
+ type: String,
5930
+ required: false
5931
+ },
5932
+ width: {
5933
+ type: Number,
5934
+ required: false
5935
+ },
5936
+ fg: {
5937
+ type: String,
5938
+ required: false
5939
+ },
5940
+ focusedFg: {
5941
+ type: String,
5942
+ required: false
5943
+ },
5944
+ focusedBg: {
5945
+ type: String,
5946
+ required: false
5947
+ },
5948
+ shortcutFg: {
5949
+ type: String,
5950
+ required: false
5951
+ }
5952
+ },
5953
+ emits: ["select", "update:focusedIndex"],
5954
+ setup(__props, { emit: __emit }) {
5955
+ const emit = __emit;
5956
+ const runtimeProps = computed(() => ({
5957
+ items: __props.items,
5958
+ focusedIndex: __props.focusedIndex,
5959
+ focused: __props.focused,
5960
+ maxHeight: __props.maxHeight,
5961
+ border: __props.border,
5962
+ width: __props.width,
5963
+ fg: __props.fg,
5964
+ focusedFg: __props.focusedFg,
5965
+ focusedBg: __props.focusedBg,
5966
+ shortcutFg: __props.shortcutFg
5967
+ }));
5968
+ function selectValue(value) {
5969
+ emit("select", value);
5970
+ }
5971
+ function updateFocusedIndex(index) {
5972
+ emit("update:focusedIndex", index);
5973
+ }
5974
+ return (_ctx, _cache) => {
5975
+ return openBlock(), createBlock(unref(Menu), mergeProps(runtimeProps.value, {
5976
+ onSelect: selectValue,
5977
+ "onUpdate:focusedIndex": updateFocusedIndex
5978
+ }), null, 16);
5979
+ };
5980
+ }
5981
+ });
5982
+ //#endregion
5502
5983
  //#region src/components/Tabs.ts
5503
5984
  /**
5504
5985
  * Tabs Component - Tab navigation
5505
5986
  */
5987
+ function tabValue(tab) {
5988
+ return Object.hasOwn(tab, "value") ? tab.value : tab.key;
5989
+ }
5506
5990
  const Tabs = defineComponent({
5507
5991
  name: "Tabs",
5508
5992
  props: {
@@ -5510,11 +5994,18 @@ const Tabs = defineComponent({
5510
5994
  type: Array,
5511
5995
  required: true
5512
5996
  },
5513
- modelValue: String,
5997
+ modelValue: null,
5998
+ by: [
5999
+ String,
6000
+ Number,
6001
+ Symbol,
6002
+ Function
6003
+ ],
5514
6004
  focused: {
5515
6005
  type: Boolean,
5516
6006
  default: false
5517
6007
  },
6008
+ maxItems: Number,
5518
6009
  position: {
5519
6010
  type: String,
5520
6011
  default: "top"
@@ -5536,22 +6027,39 @@ const Tabs = defineComponent({
5536
6027
  },
5537
6028
  emits: ["update:modelValue", "change"],
5538
6029
  setup(props, { slots, emit }) {
6030
+ const scrollOffset = ref(0);
5539
6031
  const highlightedIndex = ref(-1);
5540
6032
  const typeahead = createTypeaheadBuffer();
6033
+ const viewportSize = computed(() => {
6034
+ if (props.maxItems === void 0 || !Number.isFinite(props.maxItems)) return void 0;
6035
+ return props.maxItems > 0 ? Math.max(1, Math.floor(props.maxItems)) : void 0;
6036
+ });
5541
6037
  const syncHighlight = () => {
5542
- const activeIndex = props.tabs.findIndex((tab) => tab.key === props.modelValue && !tab.disabled);
6038
+ const activeIndex = props.tabs.findIndex((tab) => collectionValuesEqual(tabValue(tab), props.modelValue, props.by) && !tab.disabled);
5543
6039
  if (activeIndex >= 0) highlightedIndex.value = activeIndex;
5544
6040
  else if (highlightedIndex.value < 0 || highlightedIndex.value >= props.tabs.length || props.tabs[highlightedIndex.value]?.disabled) highlightedIndex.value = firstEnabledIndex(props.tabs);
5545
6041
  };
5546
- watch([() => props.modelValue, () => props.tabs], syncHighlight, {
6042
+ watch([
6043
+ () => props.modelValue,
6044
+ () => props.by,
6045
+ () => props.tabs
6046
+ ], syncHighlight, {
5547
6047
  deep: true,
5548
6048
  immediate: true
5549
6049
  });
6050
+ watch([
6051
+ highlightedIndex,
6052
+ viewportSize,
6053
+ () => props.tabs.length
6054
+ ], ([index, windowSize]) => {
6055
+ scrollOffset.value = visibleCollectionOffset(index, scrollOffset.value, windowSize, props.tabs.length);
6056
+ }, { immediate: true });
5550
6057
  const activate = () => {
5551
6058
  const tab = props.tabs[highlightedIndex.value];
5552
6059
  if (!tab || tab.disabled) return;
5553
- emit("update:modelValue", tab.key);
5554
- emit("change", tab.key);
6060
+ const value = toRaw(tabValue(tab));
6061
+ emit("update:modelValue", value);
6062
+ emit("change", value);
5555
6063
  };
5556
6064
  useInput({
5557
6065
  isActive: computed(() => props.focused),
@@ -5572,14 +6080,38 @@ const Tabs = defineComponent({
5572
6080
  },
5573
6081
  onSubmit: activate
5574
6082
  });
6083
+ const visibleTabs = computed(() => {
6084
+ const windowSize = viewportSize.value;
6085
+ const start = windowSize === void 0 ? 0 : scrollOffset.value;
6086
+ const end = windowSize === void 0 ? props.tabs.length : start + windowSize;
6087
+ return props.tabs.slice(start, end).map((tab, visibleIndex) => ({
6088
+ tab,
6089
+ index: start + visibleIndex
6090
+ }));
6091
+ });
6092
+ const scrollIndicator = computed(() => {
6093
+ const windowSize = viewportSize.value;
6094
+ if (windowSize === void 0 || props.tabs.length <= windowSize) return {
6095
+ showBefore: false,
6096
+ showAfter: false
6097
+ };
6098
+ return {
6099
+ showBefore: scrollOffset.value > 0,
6100
+ showAfter: scrollOffset.value + windowSize < props.tabs.length
6101
+ };
6102
+ });
5575
6103
  return () => {
5576
6104
  const tabItems = [];
5577
- props.tabs.forEach((tab, index) => {
5578
- if (index > 0) tabItems.push(h("text", {
6105
+ if (scrollIndicator.value.showBefore) tabItems.push(h("text", {
6106
+ key: "scroll-before",
6107
+ dim: true
6108
+ }, "…"));
6109
+ visibleTabs.value.forEach(({ tab, index }, visibleIndex) => {
6110
+ if (visibleIndex > 0) tabItems.push(h("text", {
5579
6111
  key: `sep-${index}`,
5580
6112
  dim: true
5581
6113
  }, props.separator));
5582
- const isActive = tab.key === props.modelValue;
6114
+ const isActive = collectionValuesEqual(tabValue(tab), props.modelValue, props.by);
5583
6115
  const isHighlighted = index === highlightedIndex.value;
5584
6116
  tabItems.push(h("text", {
5585
6117
  key: tab.key,
@@ -5587,9 +6119,14 @@ const Tabs = defineComponent({
5587
6119
  bg: isActive || props.focused && isHighlighted ? props.activeBg : void 0,
5588
6120
  bold: isActive,
5589
6121
  underline: isActive && props.underline || props.focused && isHighlighted,
5590
- dim: tab.disabled
6122
+ dim: tab.disabled,
6123
+ "data-tab-index": index
5591
6124
  }, tab.label));
5592
6125
  });
6126
+ if (scrollIndicator.value.showAfter) tabItems.push(h("text", {
6127
+ key: "scroll-after",
6128
+ dim: true
6129
+ }, "…"));
5593
6130
  const tabBar = h("box", {
5594
6131
  key: "tab-bar",
5595
6132
  style: {
@@ -5610,6 +6147,93 @@ const Tabs = defineComponent({
5610
6147
  }
5611
6148
  });
5612
6149
  //#endregion
6150
+ //#region src/components/FrescoTabs.vue
6151
+ var FrescoTabs_default = /* @__PURE__ */ defineComponent({
6152
+ name: "FrescoTabs",
6153
+ __name: "FrescoTabs",
6154
+ props: {
6155
+ tabs: {
6156
+ type: Array,
6157
+ required: true
6158
+ },
6159
+ modelValue: {
6160
+ type: null,
6161
+ required: false
6162
+ },
6163
+ by: {
6164
+ type: Function,
6165
+ required: false,
6166
+ skipCheck: true
6167
+ },
6168
+ focused: {
6169
+ type: Boolean,
6170
+ required: false
6171
+ },
6172
+ maxItems: {
6173
+ type: Number,
6174
+ required: false
6175
+ },
6176
+ position: {
6177
+ type: String,
6178
+ required: false
6179
+ },
6180
+ separator: {
6181
+ type: String,
6182
+ required: false
6183
+ },
6184
+ activeFg: {
6185
+ type: String,
6186
+ required: false
6187
+ },
6188
+ activeBg: {
6189
+ type: String,
6190
+ required: false
6191
+ },
6192
+ inactiveFg: {
6193
+ type: String,
6194
+ required: false
6195
+ },
6196
+ underline: {
6197
+ type: Boolean,
6198
+ required: false
6199
+ }
6200
+ },
6201
+ emits: ["update:modelValue", "change"],
6202
+ setup(__props, { emit: __emit }) {
6203
+ const emit = __emit;
6204
+ const runtimeProps = computed(() => {
6205
+ return {
6206
+ tabs: __props.tabs,
6207
+ modelValue: __props.modelValue,
6208
+ by: typeof __props.by === "function" ? (left, right) => __props.by(left, right) : __props.by,
6209
+ focused: __props.focused,
6210
+ maxItems: __props.maxItems,
6211
+ position: __props.position,
6212
+ separator: __props.separator,
6213
+ activeFg: __props.activeFg,
6214
+ activeBg: __props.activeBg,
6215
+ inactiveFg: __props.inactiveFg,
6216
+ underline: __props.underline
6217
+ };
6218
+ });
6219
+ function updateModelValue(value) {
6220
+ emit("update:modelValue", value);
6221
+ }
6222
+ function changeValue(value) {
6223
+ emit("change", value);
6224
+ }
6225
+ return (_ctx, _cache) => {
6226
+ return openBlock(), createBlock(unref(Tabs), mergeProps(runtimeProps.value, {
6227
+ "onUpdate:modelValue": updateModelValue,
6228
+ onChange: changeValue
6229
+ }), {
6230
+ default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
6231
+ _: 3
6232
+ }, 16);
6233
+ };
6234
+ }
6235
+ });
6236
+ //#endregion
5613
6237
  //#region src/components/FrescoBreadcrumb.vue?vue&type=script&setup=true&lang.ts
5614
6238
  const _hoisted_1$2 = ["fg"];
5615
6239
  const _hoisted_2$2 = [
@@ -6429,6 +7053,6 @@ var FrescoAvatar_default = /* @__PURE__ */ defineComponent({
6429
7053
  }
6430
7054
  });
6431
7055
  //#endregion
6432
- export { FrescoFrameBuffer_default as A, FrescoNewline_default as B, RadioGroup as C, TextInput as D, TextArea as E, Code as F, Grid as G, ScrollBar_default as H, createCodeBuffer as I, FrescoStack_default as J, FrescoVStack_default as K, LineNumber as L, defaultMarkdownRenderer as M, parseMarkdown as N, FrescoLink_default as O, renderMarkdown as P, FrescoTransform_default as R, Confirm as S, Select as T, Text as U, ScrollBox_default as V, FrescoCard_default as W, FrescoSpacer_default as X, FrescoDivider_default as Y, Box as Z, FrescoAlert_default as _, FrescoPopover_default as a, Form as b, FrescoBreadcrumb_default as c, Tree as d, Table as f, FrescoBadge_default as g, Timer as h, StatusBar as i, FrescoMarkdown_default as j, Diff as k, Tabs as l, FrescoTooltip_default as m, KeyHint as n, FrescoModal_default as o, List as p, FrescoHStack_default as q, FrescoHeader_default as r, Stepper as s, FrescoAvatar_default as t, Menu as u, ProgressBar as v, Checkbox as w, FrescoSlider_default as x, FrescoSpinner_default as y, FrescoStatic_default as z };
7056
+ export { FrescoFrameBuffer_default as A, FrescoNewline_default as B, RadioGroup as C, TextInput as D, TextArea as E, Code as F, Grid as G, ScrollBar_default as H, createCodeBuffer as I, FrescoStack_default as J, FrescoVStack_default as K, LineNumber as L, defaultMarkdownRenderer as M, parseMarkdown as N, FrescoLink_default as O, renderMarkdown as P, FrescoTransform_default as R, Confirm as S, FrescoSelect_default as T, Text as U, ScrollBox_default as V, FrescoCard_default as W, FrescoSpacer_default as X, FrescoDivider_default as Y, Box as Z, FrescoAlert_default as _, FrescoPopover_default as a, Form as b, FrescoBreadcrumb_default as c, FrescoTree_default as d, Table as f, FrescoBadge_default as g, Timer as h, StatusBar as i, FrescoMarkdown_default as j, Diff as k, FrescoTabs_default as l, FrescoTooltip_default as m, KeyHint as n, FrescoModal_default as o, FrescoList_default as p, FrescoHStack_default as q, FrescoHeader_default as r, Stepper as s, FrescoAvatar_default as t, FrescoMenu_default as u, ProgressBar as v, Checkbox as w, FrescoSlider_default as x, FrescoSpinner_default as y, FrescoStatic_default as z };
6433
7057
 
6434
- //# sourceMappingURL=components-DgDohiXo.mjs.map
7058
+ //# sourceMappingURL=components-B-HgL5id.mjs.map