@simon_he/vue-tui 1.0.0 → 1.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.
@@ -4466,18 +4466,45 @@ function normalizeVisibleLimit$1(value) {
4466
4466
  function isCommandPaletteRowSelectable(item) {
4467
4467
  return Boolean(item && item.kind !== "separator" && item.kind !== "group" && !item.disabled);
4468
4468
  }
4469
- function substringMatcher(item, query) {
4470
- const q = query.trim().toLowerCase();
4471
- if (!q) return { score: 0 };
4472
- const labelRanges = computeCommandPaletteMatchRanges(item.label, q);
4473
- const detailRanges = item.detail ? computeCommandPaletteMatchRanges(item.detail, q) : [];
4474
- const keywordMatch = (item.keywords ?? []).some((keyword) => String(keyword ?? "").toLowerCase().includes(q));
4475
- if (!labelRanges.length && !detailRanges.length && !keywordMatch) return null;
4476
- return {
4477
- score: labelRanges.length ? 100 : detailRanges.length ? 50 : 10,
4478
- labelRanges,
4479
- detailRanges
4480
- };
4469
+ function defaultSubstringEntries(sourceItems, query) {
4470
+ const q = query.trim();
4471
+ if (!q) return sourceItems.map((item, sourceIndex) => ({
4472
+ item,
4473
+ sourceIndex,
4474
+ match: { score: 0 }
4475
+ }));
4476
+ const needle = q.toLowerCase();
4477
+ const labelMatches = [];
4478
+ const detailMatches = [];
4479
+ const keywordMatches = [];
4480
+ for (let sourceIndex = 0; sourceIndex < sourceItems.length; sourceIndex++) {
4481
+ const item = sourceItems[sourceIndex];
4482
+ if (item.kind === "separator" || item.kind === "group") continue;
4483
+ const label = String(item.label ?? "");
4484
+ const detail = item.detail == null ? "" : String(item.detail);
4485
+ if (label.toLowerCase().includes(needle)) {
4486
+ labelMatches.push({
4487
+ item,
4488
+ sourceIndex,
4489
+ match: { score: 100 }
4490
+ });
4491
+ continue;
4492
+ }
4493
+ if (detail.toLowerCase().includes(needle)) {
4494
+ detailMatches.push({
4495
+ item,
4496
+ sourceIndex,
4497
+ match: { score: 50 }
4498
+ });
4499
+ continue;
4500
+ }
4501
+ if ((item.keywords ?? []).some((keyword) => String(keyword ?? "").toLowerCase().includes(needle))) keywordMatches.push({
4502
+ item,
4503
+ sourceIndex,
4504
+ match: { score: 10 }
4505
+ });
4506
+ }
4507
+ return labelMatches.concat(detailMatches, keywordMatches);
4481
4508
  }
4482
4509
  function fuzzyMatcher(item, query) {
4483
4510
  const q = query.trim().toLowerCase();
@@ -4732,20 +4759,26 @@ const TCommandPalette = defineComponent({
4732
4759
  const filteredEntries = computed(() => {
4733
4760
  const q = query.value.trim();
4734
4761
  const sourceItems = props.itemsProvider ? providerItems.value ?? [] : props.items;
4735
- const matcher = props.matcher ?? (props.filterStrategy === "fuzzy" ? fuzzyMatcher : substringMatcher);
4736
- const entries = sourceItems.flatMap((item, sourceIndex) => {
4737
- if (item.kind === "separator" || item.kind === "group") return q ? [] : [{
4738
- item,
4739
- sourceIndex,
4740
- match: { score: 0 }
4741
- }];
4762
+ if (!props.matcher && props.filterStrategy === "substring") return defaultSubstringEntries(sourceItems, q);
4763
+ const matcher = props.matcher ?? fuzzyMatcher;
4764
+ const entries = [];
4765
+ for (let sourceIndex = 0; sourceIndex < sourceItems.length; sourceIndex++) {
4766
+ const item = sourceItems[sourceIndex];
4767
+ if (item.kind === "separator" || item.kind === "group") {
4768
+ if (!q) entries.push({
4769
+ item,
4770
+ sourceIndex,
4771
+ match: { score: 0 }
4772
+ });
4773
+ continue;
4774
+ }
4742
4775
  const match = matcher(item, q);
4743
- return match ? [{
4776
+ if (match) entries.push({
4744
4777
  item,
4745
4778
  sourceIndex,
4746
4779
  match
4747
- }] : [];
4748
- });
4780
+ });
4781
+ }
4749
4782
  entries.sort((a, b) => b.match.score - a.match.score || a.sourceIndex - b.sourceIndex);
4750
4783
  return entries;
4751
4784
  });
@@ -5383,6 +5416,40 @@ const TSelect = defineComponent({
5383
5416
  const typeaheadQuery = ref("");
5384
5417
  const query = computed(() => props.query ?? innerQuery.value);
5385
5418
  const options = computed(() => providerOptions.value ?? props.options);
5419
+ function getOptionLabel(opt) {
5420
+ return isOptionObject(opt) ? opt.label : opt;
5421
+ }
5422
+ function getOptionKind(opt) {
5423
+ return isOptionObject(opt) ? opt.kind : void 0;
5424
+ }
5425
+ function getOptionValue(opt, index) {
5426
+ if (props.valueMode === "index") return index;
5427
+ if (props.valueMode === "option") return opt;
5428
+ return isOptionObject(opt) && "value" in opt ? opt.value : getOptionLabel(opt);
5429
+ }
5430
+ const optionValueIndex = computed(() => {
5431
+ if (props.valueMode === "index") return null;
5432
+ const indexed = new Map();
5433
+ const opts = options.value;
5434
+ for (let index = 0; index < opts.length; index++) {
5435
+ const value = getOptionValue(opts[index], index);
5436
+ if (typeof value === "number" && value === 0) continue;
5437
+ if (!indexed.has(value)) indexed.set(value, index);
5438
+ }
5439
+ return indexed;
5440
+ });
5441
+ function valuesEqual(a, b) {
5442
+ return Object.is(a, b);
5443
+ }
5444
+ function modelIndex(value, useValueIndex = props.multiple) {
5445
+ if (props.valueMode === "index") return normalizeModelIndex(value);
5446
+ if (useValueIndex) {
5447
+ const indexed = optionValueIndex.value;
5448
+ const cached = indexed?.get(value);
5449
+ if (cached != null) return cached;
5450
+ }
5451
+ return options.value.findIndex((opt, optIndex) => valuesEqual(getOptionValue(opt, optIndex), value));
5452
+ }
5386
5453
  const initialActive = (() => {
5387
5454
  const max = Math.max(0, options.value.length - 1);
5388
5455
  if (!props.multiple) {
@@ -5442,24 +5509,6 @@ const TSelect = defineComponent({
5442
5509
  }
5443
5510
  innerActive.value = clamp(active.value, 0, max);
5444
5511
  });
5445
- function getOptionLabel(opt) {
5446
- return isOptionObject(opt) ? opt.label : opt;
5447
- }
5448
- function getOptionKind(opt) {
5449
- return isOptionObject(opt) ? opt.kind : void 0;
5450
- }
5451
- function getOptionValue(opt, index) {
5452
- if (props.valueMode === "index") return index;
5453
- if (props.valueMode === "option") return opt;
5454
- return isOptionObject(opt) && "value" in opt ? opt.value : getOptionLabel(opt);
5455
- }
5456
- function valuesEqual(a, b) {
5457
- return Object.is(a, b);
5458
- }
5459
- function modelIndex(value) {
5460
- if (props.valueMode === "index") return normalizeModelIndex(value);
5461
- return options.value.findIndex((opt, optIndex) => valuesEqual(getOptionValue(opt, optIndex), value));
5462
- }
5463
5512
  function isOptionInteractive(opt) {
5464
5513
  if (!opt) return false;
5465
5514
  const kind = getOptionKind(opt);
@@ -7,7 +7,7 @@ import { SUPPRESS_TERMINAL_POINTER_DOWN, SUPPRESS_TERMINAL_POINTER_MOVE, SUPPRES
7
7
  import { createFramePerfStore, createTraceStore } from "./trace-CjzYeIxN.js";
8
8
  import { envFlag, envString, firstNonEmptyEnv } from "./env-Cl8qmNFN.js";
9
9
  import { createTerminalSelectionController } from "./terminal-selection-DBinO6p5.js";
10
- import { TBox, TInput, TRenderPlane, TText, clamp, fitCellText, mergeStyle, repeatToCells, resolveOverlayPlacement } from "./TSelect-BwJJbuTt.js";
10
+ import { TBox, TInput, TRenderPlane, TText, clamp, fitCellText, mergeStyle, repeatToCells, resolveOverlayPlacement } from "./TSelect-HzILuti9.js";
11
11
  import { framePerfNow, mergeFramePerfReason } from "./frame-perf-C6Xfg85O.js";
12
12
  import { computed, defineComponent, effectScope, getCurrentInstance, h, inject, nextTick, onBeforeUnmount, onMounted, onScopeDispose, onUnmounted, provide, ref, shallowReactive, shallowRef, toRef, watch, watchEffect } from "vue";
13
13
 
@@ -2991,6 +2991,17 @@ function rowKey$1(row, index, key) {
2991
2991
  function sameRowKey(a, b) {
2992
2992
  return Object.is(a, b);
2993
2993
  }
2994
+ function selectedRowKeyIndex(keys) {
2995
+ if (!keys?.length) return null;
2996
+ const indexed = new Set();
2997
+ for (let index = 0; index < keys.length; index++) {
2998
+ if (!(index in keys)) continue;
2999
+ const key = keys[index];
3000
+ if (typeof key === "number" && key === 0) return null;
3001
+ indexed.add(key);
3002
+ }
3003
+ return indexed;
3004
+ }
2994
3005
  const TTable = defineComponent({
2995
3006
  name: "TTable",
2996
3007
  props: {
@@ -3099,10 +3110,19 @@ const TTable = defineComponent({
3099
3110
  const rowStyle = computed(() => mergeStyle(defaultStyle.value, theme.value.components.TTable?.rowStyle, props.style));
3100
3111
  const selectedRowStyle = computed(() => mergeStyle(rowStyle.value, theme.value.components.TTable?.selectedStyle, props.selectedStyle));
3101
3112
  const activeRowStyle = computed(() => mergeStyle(theme.value.components.TTable?.activeStyle, props.activeStyle));
3113
+ const selectedRowKeysIndex = computed(() => selectedRowKeyIndex(props.selectedRowKeys));
3102
3114
  const bodyRows = computed(() => {
3103
3115
  const top = props.header ? 2 : 0;
3104
3116
  return props.rows.slice(0, Math.max(0, props.h - top));
3105
3117
  });
3118
+ function isRowSelected(key) {
3119
+ if (props.selectedRowKey !== void 0 && sameRowKey(key, props.selectedRowKey)) return true;
3120
+ const selectedKeys = props.selectedRowKeys;
3121
+ if (!selectedKeys?.length) return false;
3122
+ const indexed = selectedRowKeysIndex.value;
3123
+ if (indexed) return indexed.has(key);
3124
+ return selectedKeys.some((candidate) => sameRowKey(candidate, key));
3125
+ }
3106
3126
  return () => {
3107
3127
  const children = [];
3108
3128
  if (props.header && props.h > 0) {
@@ -3169,7 +3189,7 @@ const TTable = defineComponent({
3169
3189
  const row = bodyRows.value[index];
3170
3190
  const y = (props.header ? 2 : 0) + index;
3171
3191
  const key = rowKey$1(row, index, props.rowKey);
3172
- const selected = props.selectedRowKey !== void 0 && sameRowKey(key, props.selectedRowKey) || Boolean(props.selectedRowKeys?.some((candidate) => sameRowKey(candidate, key)));
3192
+ const selected = isRowSelected(key);
3173
3193
  const active = props.activeRowKey !== void 0 && sameRowKey(key, props.activeRowKey);
3174
3194
  children.push(h(TView, {
3175
3195
  key: `row:${String(key)}`,
@@ -3387,34 +3407,45 @@ const TDataTable = defineComponent({
3387
3407
  },
3388
3408
  setup(props, { emit }) {
3389
3409
  const innerScrollTop = ref(0);
3390
- const indexedRows = computed(() => props.rows.map((row, originalIndex) => ({
3391
- row,
3392
- originalIndex
3393
- })));
3410
+ const hasLocalFilter = computed(() => !props.manualFilter && props.filterable && Boolean(props.filter.trim()));
3411
+ const hasLocalSort = computed(() => !props.manualSort && props.sortable && Boolean(props.sortBy));
3412
+ const indexedRows = computed(() => {
3413
+ if (!hasLocalFilter.value && !hasLocalSort.value) return null;
3414
+ return props.rows.map((row, originalIndex) => ({
3415
+ row,
3416
+ originalIndex
3417
+ }));
3418
+ });
3394
3419
  const filteredRows = computed(() => {
3395
- if (props.manualFilter) return indexedRows.value;
3396
- if (!props.filterable || !props.filter.trim()) return indexedRows.value;
3420
+ const rows = indexedRows.value;
3421
+ if (!rows) return null;
3422
+ if (!hasLocalFilter.value) return rows;
3397
3423
  const query = props.filter.trim().toLowerCase();
3398
- if (props.filterPredicate) return indexedRows.value.filter(({ row, originalIndex }) => props.filterPredicate(row, query, props.columns, originalIndex));
3399
- return indexedRows.value.filter(({ row, originalIndex }) => props.columns.some((column) => displayValue(column, row, originalIndex).toLowerCase().includes(query)));
3424
+ if (props.filterPredicate) return rows.filter(({ row, originalIndex }) => props.filterPredicate(row, query, props.columns, originalIndex));
3425
+ return rows.filter(({ row, originalIndex }) => props.columns.some((column) => displayValue(column, row, originalIndex).toLowerCase().includes(query)));
3400
3426
  });
3401
3427
  const sortedRows = computed(() => {
3402
- const rows = [...filteredRows.value];
3403
- if (props.manualSort || !props.sortable || !props.sortBy) return rows;
3428
+ const rows = filteredRows.value;
3429
+ if (!rows) return null;
3430
+ if (!hasLocalSort.value) return rows;
3431
+ const sorted = [...rows];
3404
3432
  const column = props.columns.find((candidate) => candidate.key === props.sortBy);
3405
- rows.sort((a, b) => {
3433
+ sorted.sort((a, b) => {
3406
3434
  const result = props.sorter && column ? props.sorter(a.row, b.row, column) : compareValues(a.row[props.sortBy], b.row[props.sortBy]);
3407
3435
  return props.sortDirection === "desc" ? -result : result;
3408
3436
  });
3409
- return rows;
3437
+ return sorted;
3410
3438
  });
3411
3439
  function nonNegativeInteger(value) {
3412
3440
  const n = Math.floor(Number(value));
3413
3441
  return Number.isFinite(n) ? Math.max(0, n) : 0;
3414
3442
  }
3415
3443
  const visibleRowCapacity = computed(() => Math.max(0, nonNegativeInteger(props.h) - 2));
3444
+ function processedRowCount() {
3445
+ return sortedRows.value?.length ?? props.rows.length;
3446
+ }
3416
3447
  function maxScrollTop$1() {
3417
- return Math.max(0, sortedRows.value.length - visibleRowCapacity.value);
3448
+ return Math.max(0, processedRowCount() - visibleRowCapacity.value);
3418
3449
  }
3419
3450
  function clampScrollTop$1(value) {
3420
3451
  return Math.min(maxScrollTop$1(), nonNegativeInteger(value));
@@ -3430,7 +3461,28 @@ const TDataTable = defineComponent({
3430
3461
  watch(() => [normalizedScrollTop.value, requestedScrollTop.value], ([next, requested]) => {
3431
3462
  if (next !== requested) setScrollTop(next);
3432
3463
  }, { immediate: true });
3433
- const visibleRows = computed(() => sortedRows.value.slice(normalizedScrollTop.value, normalizedScrollTop.value + visibleRowCapacity.value));
3464
+ function dataEntryAt(absoluteIndex) {
3465
+ const rows = sortedRows.value;
3466
+ if (rows) return rows[absoluteIndex];
3467
+ const row = props.rows[absoluteIndex];
3468
+ return row ? {
3469
+ row,
3470
+ originalIndex: absoluteIndex
3471
+ } : void 0;
3472
+ }
3473
+ function originalIndexForAbsoluteIndex(absoluteIndex) {
3474
+ return dataEntryAt(absoluteIndex)?.originalIndex ?? absoluteIndex;
3475
+ }
3476
+ const visibleRows = computed(() => {
3477
+ const top = normalizedScrollTop.value;
3478
+ const bottom = top + visibleRowCapacity.value;
3479
+ const rows = sortedRows.value;
3480
+ if (rows) return rows.slice(top, bottom);
3481
+ return props.rows.slice(top, bottom).map((row, index) => ({
3482
+ row,
3483
+ originalIndex: top + index
3484
+ }));
3485
+ });
3434
3486
  const tableRows = computed(() => visibleRows.value.map(({ row }) => row));
3435
3487
  const activeAbsoluteIndex = ref(null);
3436
3488
  const activeRowIdentity = ref(NO_ACTIVE_ROW);
@@ -3438,7 +3490,7 @@ const TDataTable = defineComponent({
3438
3490
  const columns = computed(() => props.columns.map((column) => {
3439
3491
  const format = column.format ? (value, row, visibleIndex) => {
3440
3492
  const absoluteIndex = normalizedScrollTop.value + visibleIndex;
3441
- const originalIndex = sortedRows.value[absoluteIndex]?.originalIndex ?? absoluteIndex;
3493
+ const originalIndex = originalIndexForAbsoluteIndex(absoluteIndex);
3442
3494
  return column.format(value, row, originalIndex);
3443
3495
  } : void 0;
3444
3496
  if (!props.sortable || column.key !== props.sortBy) return format ? {
@@ -3454,7 +3506,7 @@ const TDataTable = defineComponent({
3454
3506
  }));
3455
3507
  function originalIndexAt(index) {
3456
3508
  const absoluteIndex = normalizedScrollTop.value + index;
3457
- return sortedRows.value[absoluteIndex]?.originalIndex ?? absoluteIndex;
3509
+ return originalIndexForAbsoluteIndex(absoluteIndex);
3458
3510
  }
3459
3511
  function dataTableRowKey(row, index) {
3460
3512
  return rowKey(row, originalIndexAt(index), props.rowKey);
@@ -3472,18 +3524,42 @@ const TDataTable = defineComponent({
3472
3524
  return new Set(props.selectedRowKey === void 0 ? [] : [props.selectedRowKey]);
3473
3525
  }
3474
3526
  const activeRowKey = computed(() => activeRowIdentity.value === NO_ACTIVE_ROW ? void 0 : activeRowIdentity.value);
3475
- watch(() => [props.rowKey, ...sortedRows.value.map((entry) => dataRowKey(entry))], () => {
3527
+ const activeRawRowKey = computed(() => {
3528
+ if (activeAbsoluteIndex.value == null || sortedRows.value) return NO_ACTIVE_ROW;
3529
+ const index = activeAbsoluteIndex.value;
3530
+ const row = props.rows[index];
3531
+ return row ? rowKey(row, index, props.rowKey) : NO_ACTIVE_ROW;
3532
+ });
3533
+ function findDataIndexByKey(key) {
3534
+ const rows = sortedRows.value;
3535
+ if (rows) return rows.findIndex((entry) => Object.is(dataRowKey(entry), key));
3536
+ return props.rows.findIndex((row, index) => Object.is(rowKey(row, index, props.rowKey), key));
3537
+ }
3538
+ watch(() => [
3539
+ props.rowKey,
3540
+ props.rows,
3541
+ sortedRows.value,
3542
+ activeRowIdentity.value
3543
+ ], () => {
3476
3544
  if (activeRowIdentity.value === NO_ACTIVE_ROW) return;
3477
- const nextIndex = sortedRows.value.findIndex((entry) => Object.is(dataRowKey(entry), activeRowIdentity.value));
3545
+ const nextIndex = findDataIndexByKey(activeRowIdentity.value);
3478
3546
  if (nextIndex < 0) {
3479
3547
  clearActiveRow();
3480
3548
  return;
3481
3549
  }
3482
3550
  activeAbsoluteIndex.value = nextIndex;
3483
3551
  });
3552
+ watch(() => [props.rowKey, activeRawRowKey.value], ([, key]) => {
3553
+ if (activeRowIdentity.value === NO_ACTIVE_ROW) return;
3554
+ if (key === NO_ACTIVE_ROW) {
3555
+ clearActiveRow();
3556
+ return;
3557
+ }
3558
+ if (!Object.is(activeRowIdentity.value, key)) activeRowIdentity.value = key;
3559
+ });
3484
3560
  function setActiveAbsoluteIndex(absoluteIndex) {
3485
- const clamped = Math.max(0, Math.min(sortedRows.value.length - 1, absoluteIndex));
3486
- const entry = sortedRows.value[clamped];
3561
+ const clamped = Math.max(0, Math.min(processedRowCount() - 1, absoluteIndex));
3562
+ const entry = dataEntryAt(clamped);
3487
3563
  if (!entry) {
3488
3564
  clearActiveRow();
3489
3565
  return null;
@@ -3550,7 +3626,7 @@ const TDataTable = defineComponent({
3550
3626
  if (event.key === "Enter" || event.key === " ") {
3551
3627
  const active = setActiveAbsoluteIndex(current);
3552
3628
  if (!active) return;
3553
- const entry = sortedRows.value[active.dataIndex];
3629
+ const entry = dataEntryAt(active.dataIndex);
3554
3630
  if (!entry) return;
3555
3631
  const visibleIndex = active.dataIndex - active.scrollTop;
3556
3632
  commitSelection(entry, Math.max(0, Math.min(Math.max(0, visibleRowCapacity.value - 1), visibleIndex)), active.dataIndex);
@@ -6315,8 +6391,10 @@ const TLinkifyText = defineComponent({
6315
6391
 
6316
6392
  //#endregion
6317
6393
  //#region src/vue/components/TTree.ts
6318
- function flattenTree(nodes, expanded, depth = 0, out = []) {
6394
+ function flattenTree(nodes, expanded, limit, depth = 0, out = []) {
6395
+ if (out.length >= limit) return out;
6319
6396
  for (const node of nodes) {
6397
+ if (out.length >= limit) break;
6320
6398
  const expandable = Boolean(node.children?.length);
6321
6399
  const isExpanded = expanded.has(node.id);
6322
6400
  out.push({
@@ -6325,10 +6403,14 @@ function flattenTree(nodes, expanded, depth = 0, out = []) {
6325
6403
  expandable,
6326
6404
  expanded: isExpanded
6327
6405
  });
6328
- if (expandable && isExpanded) flattenTree(node.children, expanded, depth + 1, out);
6406
+ if (expandable && isExpanded) flattenTree(node.children, expanded, limit, depth + 1, out);
6329
6407
  }
6330
6408
  return out;
6331
6409
  }
6410
+ function visibleRowLimit(height) {
6411
+ const limit = Math.floor(height);
6412
+ return Number.isFinite(limit) ? Math.max(0, limit) : 0;
6413
+ }
6332
6414
  const TTree = defineComponent({
6333
6415
  name: "TTree",
6334
6416
  props: {
@@ -6395,7 +6477,7 @@ const TTree = defineComponent({
6395
6477
  const { defaultStyle } = useTerminal();
6396
6478
  const baseStyle = computed(() => mergeStyle(defaultStyle.value, props.style));
6397
6479
  const expandedSet = computed(() => new Set(props.expandedIds));
6398
- const rows = computed(() => flattenTree(props.nodes, expandedSet.value).slice(0, props.h));
6480
+ const rows = computed(() => flattenTree(props.nodes, expandedSet.value, visibleRowLimit(props.h)));
6399
6481
  function toggle(item) {
6400
6482
  if (item.node.disabled || !item.expandable) return;
6401
6483
  const ids = new Set(props.expandedIds);