uicore-ts 1.1.355 → 1.1.357

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.
@@ -53,7 +53,11 @@ const _UIAutocompleteTextField = class extends import_UITextField.UITextField {
53
53
  this._dropdownView.highlightedRowIndex = matchIndex;
54
54
  }
55
55
  };
56
- this.controlEventTargetAccumulator.Blur = () => {
56
+ this.controlEventTargetAccumulator.Blur = (sender, event) => {
57
+ const focusEvent = event;
58
+ if ((0, import_UIObject.IS)(focusEvent.relatedTarget) && this._dropdownView.viewHTMLElement.contains(focusEvent.relatedTarget)) {
59
+ return;
60
+ }
57
61
  this.closeDropdown();
58
62
  };
59
63
  this.addTargetForControlEvent(import_UITextField.UITextField.controlEvent.TextChange, () => {
@@ -225,7 +229,13 @@ const _UIAutocompleteTextField = class extends import_UITextField.UITextField {
225
229
  }
226
230
  this._isDropdownOpen = import_UIObject.YES;
227
231
  this._dropdownView.filterWords = [];
228
- this.updateFilteredItems();
232
+ this._dropdownView.filteredItems = this._autocompleteItems;
233
+ if (this._dropdownView.filteredItems.length > 0) {
234
+ const matchIndex = this._autocompleteItems.findIndex(
235
+ (item) => item.label === this.text
236
+ );
237
+ this._dropdownView.highlightedRowIndex = matchIndex !== -1 ? matchIndex : 0;
238
+ }
229
239
  this._dropdownView.showAnchoredToView(this);
230
240
  }
231
241
  closeDropdown() {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../scripts/UIAutocompleteTextField.ts"],
4
- "sourcesContent": ["import { UIAutocompleteDropdownView } from \"./UIAutocompleteDropdownView\"\nimport { UIAutocompleteItem } from \"./UIAutocompleteRowView\"\nimport { IS, IS_NOT, NO, YES } from \"./UIObject\"\nimport { UITextField } from \"./UITextField\"\nimport { UIView, UIViewAddControlEventTargetObject } from \"./UIView\"\n\n\nexport class UIAutocompleteTextField<T = string> extends UITextField {\n \n _autocompleteItems: UIAutocompleteItem<T>[] = []\n _selectedItem?: UIAutocompleteItem<T>\n _dropdownView: UIAutocompleteDropdownView<T>\n _isDropdownOpen: boolean = NO\n _strictSelection: boolean = NO\n _isValid: boolean = YES\n // Set to YES only when the user has explicitly interacted with the dropdown\n // (typed text or navigated with arrow keys). Prevents Tab-to-focus from\n // auto-committing the first item.\n _userHasNavigatedDropdown: boolean = NO\n \n /**\n * When YES, the filter text is split on whitespace and all words must appear\n * in the item label (AND logic). When NO (default), the full filter string is\n * matched as a single substring.\n */\n usesMultiWordAndSearch: boolean = NO\n \n \n static override controlEvent = Object.assign({}, UITextField.controlEvent, {\n \"SelectionDidChange\": \"SelectionDidChange\"\n })\n \n override get controlEventTargetAccumulator(): UIViewAddControlEventTargetObject<typeof UIAutocompleteTextField> {\n return (super.controlEventTargetAccumulator as any)\n }\n \n \n constructor(elementID?: string) {\n \n super(elementID)\n \n this._dropdownView = this.newDropdownView()\n \n this._dropdownView.didSelectItem = (item) => {\n this.commitSelection(item)\n }\n \n let textBeforeFocus = this.text\n let itemBeforeFocus = this.selectedItem\n \n // Open dropdown on focus.\n // If a selection is already committed we keep the confirmed text visible\n // and do not clear it \u2014 this covers Tab-into-field after a prior selection,\n // as well as returning focus after commitSelection.\n this.controlEventTargetAccumulator.Focus = () => {\n textBeforeFocus = this.text\n itemBeforeFocus = this.selectedItem\n this._userHasNavigatedDropdown = NO\n this.openDropdown()\n this.textElementView.viewHTMLElement.select()\n const matchIndex = this._dropdownView.filteredItems.findIndex(\n item => item.label === textBeforeFocus\n )\n if (matchIndex !== -1) {\n this._dropdownView.highlightedRowIndex = matchIndex\n }\n }\n \n // Close on blur\n this.controlEventTargetAccumulator.Blur = () => {\n this.closeDropdown()\n }\n \n // Filter on text change\n this.addTargetForControlEvent(UITextField.controlEvent.TextChange, () => {\n this._selectedItem = undefined\n this._userHasNavigatedDropdown = this.text.length > 0\n this.updateFilteredItems()\n if (!this._isDropdownOpen) {\n this.openDropdown()\n }\n })\n \n // Keyboard navigation: down arrow\n this.textElementView.addTargetForControlEvent(UIView.controlEvent.DownArrowDown, (sender, event) => {\n event.preventDefault()\n if (!this._isDropdownOpen) {\n this.openDropdown()\n return\n }\n this._userHasNavigatedDropdown = YES\n const maxIndex = this._dropdownView.filteredItems.length - 1\n if (this._dropdownView.highlightedRowIndex < maxIndex) {\n this._dropdownView.highlightedRowIndex = this._dropdownView.highlightedRowIndex + 1\n }\n })\n \n // Keyboard navigation: up arrow\n this.textElementView.addTargetForControlEvent(UIView.controlEvent.UpArrowDown, (sender, event) => {\n event.preventDefault()\n this._userHasNavigatedDropdown = YES\n if (this._dropdownView.highlightedRowIndex > 0) {\n this._dropdownView.highlightedRowIndex = this._dropdownView.highlightedRowIndex - 1\n }\n })\n \n // Enter: commit focused item\n this.addTargetForControlEvent(UIView.controlEvent.EnterDown, () => {\n const highlightedItem = this._dropdownView.highlightedItem\n if (IS(highlightedItem)) {\n this.commitSelection(highlightedItem)\n }\n else if (this._isDropdownOpen) {\n this.closeDropdown()\n }\n })\n \n // Tab: commit highlighted item only if the user has explicitly interacted\n // with the dropdown (typed or used arrow keys). This prevents tabbing\n // through the form from silently committing the first suggestion.\n // When the user has not navigated, we do NOT consume the event \u2014 the\n // dropdown will close via the Blur handler and RHPageFocusManager will\n // handle the Tab navigation normally.\n this.addTargetForControlEvent(UIView.controlEvent.TabDown, (sender, event) => {\n if (this._isDropdownOpen && this._userHasNavigatedDropdown) {\n const highlightedItem = this._dropdownView.highlightedItem\n if (IS(highlightedItem)) {\n event.preventDefault()\n this.commitSelection(highlightedItem)\n }\n }\n })\n \n // Escape: dismiss dropdown\n this.addTargetForControlEvent(UIView.controlEvent.EscDown, () => {\n if (this._isDropdownOpen) {\n this.closeDropdown()\n if (this.strictSelection) {\n this.commitSelection(itemBeforeFocus as any)\n }\n else {\n this.text = textBeforeFocus\n }\n }\n })\n \n }\n \n \n /** Override in subclass to provide a custom dropdown view. */\n newDropdownView(): UIAutocompleteDropdownView<T> {\n return new UIAutocompleteDropdownView<T>(\n this.elementID ? this.elementID + \"Dropdown\" : undefined\n )\n }\n \n \n // MARK: - Selection\n \n get selectedItem(): T | undefined {\n return this._selectedItem?.value\n }\n \n \n get strictSelection(): boolean {\n return this._strictSelection\n }\n \n set strictSelection(strict: boolean) {\n this._strictSelection = strict\n this.updateValidationVisuals()\n }\n \n \n commitSelection(item: UIAutocompleteItem<T>) {\n \n // Set the selection state and close the dropdown without blurring.\n // Keeping focus on this view means the next Tab press is handled by\n // our TabDown handler rather than being picked up natively by the browser.\n this._selectedItem = item\n this.text = item.label\n this.closeDropdown()\n this.updateValidationVisuals()\n this.sendControlEventForKey(UIAutocompleteTextField.controlEvent.SelectionDidChange)\n \n }\n \n \n // MARK: - Data\n \n /** Convenience: set string suggestions. Each string becomes { label: s, value: s }. */\n set autocompleteStrings(strings: string[]) {\n this._autocompleteItems = strings.map(s => ({\n label: s,\n value: s as unknown as T\n }))\n this.updateFilteredItems()\n }\n \n get autocompleteStrings(): string[] {\n return this._autocompleteItems.map(item => item.label)\n }\n \n set autocompleteData(items: UIAutocompleteItem<T>[]) {\n this._autocompleteItems = items\n this.updateFilteredItems()\n }\n \n get autocompleteData(): UIAutocompleteItem<T>[] {\n return this._autocompleteItems\n }\n \n \n // MARK: - Filtering\n \n /**\n * Splits the given lowercase-trimmed filter text into individual words when\n * usesMultiWordAndSearch is YES, or returns it as a single-element array otherwise.\n * Returns an empty array when the input is empty.\n */\n _filterWordsFromText(filterText: string): string[] {\n if (filterText.length === 0) {\n return []\n }\n if (this.usesMultiWordAndSearch) {\n return filterText.split(/\\s+/).filter(word => word.length > 0)\n }\n return [filterText]\n }\n \n \n /**\n * Returns true when the given label (already lowercased) satisfies all filter\n * words \u2014 i.e. every word appears somewhere in the label.\n */\n _labelMatchesFilterWords(label: string, filterWords: string[]): boolean {\n return filterWords.every(word => label.includes(word))\n }\n \n \n /**\n * Returns true when the character immediately before `position` in `label`\n * is a word separator (or the position is at the start of the string).\n * Used to give a bonus to matches that start at a word boundary.\n */\n _isWordBoundary(label: string, position: number): boolean {\n if (position === 0) {\n return YES\n }\n const charBefore = label[position - 1]\n return \" -/\\\\|._,;:()[]\".includes(charBefore)\n }\n \n \n /**\n * Scores a label against the filter words. Lower score = better match.\n *\n * Scoring factors (in priority order):\n * 1. Non-sequential penalty \u2014 words must appear in typed order to avoid\n * a large penalty that pushes them below all sequential matches.\n * 2. Per-word boundary score \u2014 for each filter word, a mid-word match\n * scores worse than a word-boundary match. The sum across all words\n * determines the boundary tier.\n * 3. Position of the first matched word \u2014 within the same boundary tier,\n * earlier appearances rank higher.\n * 4. Total label length \u2014 shorter labels are more specific (tiebreaker).\n *\n * Example: query \"p\u00F5 pu\"\n * \"P\u00F5hjavee puhastusvahendid\" \u2192 \"p\u00F5\" at boundary(0), \"pu\" at boundary(9) \u2192 low boundary score\n * \"p\u00F5randapuhastusvahendid\" \u2192 \"p\u00F5\" at boundary(0), \"pu\" mid-word(7) \u2192 higher boundary score\n * \u2192 \"P\u00F5hjavee puhastusvahendid\" ranks first.\n */\n _scoreLabel(label: string, filterWords: string[]): number {\n \n if (filterWords.length === 0) {\n return label.length\n }\n \n // --- Sequential check ---\n // Scan left-to-right; if all words appear in order record the positions.\n let cursor = 0\n let isSequential = YES\n const sequentialPositions: number[] = []\n for (const word of filterWords) {\n const position = label.indexOf(word, cursor)\n if (position === -1) {\n isSequential = NO\n break\n }\n sequentialPositions.push(position)\n cursor = position + word.length\n }\n \n // --- Boundary score ---\n // For each filter word find its best (leftmost) match and check whether\n // it lands on a word boundary. Non-boundary matches incur a per-word\n // penalty of 1, so the boundary score is 0..filterWords.length.\n let boundaryScore = 0\n for (const word of filterWords) {\n const position = label.indexOf(word)\n if (position !== -1 && !this._isWordBoundary(label, position)) {\n boundaryScore += 1\n }\n }\n \n // Position of the first word's earliest match.\n const firstMatchPosition = label.indexOf(filterWords[0])\n \n // Compose score \u2014 each tier must not overflow into the next:\n // Non-sequential penalty : 10 000 000 (dominates everything)\n // Boundary score : 10 000 (per word, max ~10 words \u2192 100 000 max, safe)\n // First-match position : 100 (labels rarely exceed 200 chars)\n // Label length : 1 (tiebreaker)\n const sequentialPenalty = isSequential ? 0 : 10_000_000\n \n return sequentialPenalty +\n boundaryScore * 10_000 +\n firstMatchPosition * 100 +\n label.length\n \n }\n \n \n updateFilteredItems() {\n \n const rawFilterText = this.text.toLowerCase().trim()\n const filterWords = this._filterWordsFromText(rawFilterText)\n \n let filtered: UIAutocompleteItem<T>[]\n \n if (filterWords.length === 0) {\n filtered = this._autocompleteItems\n }\n else {\n filtered = this._autocompleteItems\n .filter(item => this._labelMatchesFilterWords(item.label.toLowerCase(), filterWords))\n .map((item, originalIndex) => ({ item, originalIndex, score: this._scoreLabel(item.label.toLowerCase(), filterWords) }))\n .sort((a, b) => a.score - b.score || a.originalIndex - b.originalIndex)\n .map(({ item }) => item)\n }\n \n // If the only remaining result is an exact match for the current text,\n // the user has already made their selection \u2014 no need to show the dropdown.\n const isExactSingleMatch = filtered.length === 1 &&\n filtered[0].label.toLowerCase() === rawFilterText\n \n this._dropdownView.filterWords = filterWords\n this._dropdownView.filteredItems = isExactSingleMatch ? [] : filtered\n \n if (this._dropdownView.filteredItems.length > 0) {\n this._dropdownView.highlightedRowIndex = 0\n }\n \n if (this._isDropdownOpen) {\n this._dropdownView.showAnchoredToView(this)\n }\n \n }\n \n \n // MARK: - Dropdown Lifecycle\n \n openDropdown() {\n \n if (this._isDropdownOpen) {\n return\n }\n \n this._isDropdownOpen = YES\n this._dropdownView.filterWords = []\n this.updateFilteredItems()\n this._dropdownView.showAnchoredToView(this)\n \n }\n \n closeDropdown() {\n \n if (!this._isDropdownOpen) {\n return\n }\n \n this._isDropdownOpen = NO\n this._dropdownView.dismiss()\n \n // In strict mode, clear text if it doesn't match any item\n if (this._strictSelection && IS_NOT(this._selectedItem)) {\n const currentText = this.text.trim()\n if (currentText.length > 0) {\n const matchingItem = this._autocompleteItems.find(\n item => item.label === currentText\n )\n if (IS(matchingItem)) {\n this._selectedItem = matchingItem\n }\n else {\n this.text = \"\"\n this._selectedItem = undefined\n this.sendControlEventForKey(UITextField.controlEvent.TextChange)\n }\n }\n }\n \n this.updateValidationVisuals()\n \n }\n \n \n // MARK: - Validation\n \n /** Whether the current text is valid given the strictSelection setting. */\n get isValid(): boolean {\n \n if (!this._strictSelection) {\n return YES\n }\n \n const currentText = this.text.trim()\n \n if (currentText.length === 0) {\n return YES\n }\n \n return this._autocompleteItems.some(item => item.label === currentText)\n \n }\n \n \n /**\n * Hook for subclasses to apply custom visual styling based on validation state.\n * Called after dropdown closes and after selection changes.\n */\n updateValidationVisuals() {\n // Base implementation does nothing. Subclasses override.\n }\n \n \n // MARK: - Cleanup\n \n override wasRemovedFromViewTree() {\n \n super.wasRemovedFromViewTree()\n \n this._dropdownView.removeFromSuperview()\n \n }\n \n \n // MARK: - Layout\n \n override layoutSubviews() {\n \n super.layoutSubviews()\n \n if (this._isDropdownOpen) {\n this._dropdownView.showAnchoredToView(this)\n }\n \n }\n \n \n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { UIAutocompleteDropdownView } from \"./UIAutocompleteDropdownView\"\nimport { UIAutocompleteItem } from \"./UIAutocompleteRowView\"\nimport { IS, IS_NOT, NO, YES } from \"./UIObject\"\nimport { UITextField } from \"./UITextField\"\nimport { UIView, UIViewAddControlEventTargetObject } from \"./UIView\"\n\n\nexport class UIAutocompleteTextField<T = string> extends UITextField {\n \n _autocompleteItems: UIAutocompleteItem<T>[] = []\n _selectedItem?: UIAutocompleteItem<T>\n _dropdownView: UIAutocompleteDropdownView<T>\n _isDropdownOpen: boolean = NO\n _strictSelection: boolean = NO\n _isValid: boolean = YES\n // Set to YES only when the user has explicitly interacted with the dropdown\n // (typed text or navigated with arrow keys). Prevents Tab-to-focus from\n // auto-committing the first item.\n _userHasNavigatedDropdown: boolean = NO\n \n /**\n * When YES, the filter text is split on whitespace and all words must appear\n * in the item label (AND logic). When NO (default), the full filter string is\n * matched as a single substring.\n */\n usesMultiWordAndSearch: boolean = NO\n \n \n static override controlEvent = Object.assign({}, UITextField.controlEvent, {\n \"SelectionDidChange\": \"SelectionDidChange\"\n })\n \n override get controlEventTargetAccumulator(): UIViewAddControlEventTargetObject<typeof UIAutocompleteTextField> {\n return (super.controlEventTargetAccumulator as any)\n }\n \n \n constructor(elementID?: string) {\n \n super(elementID)\n \n this._dropdownView = this.newDropdownView()\n \n this._dropdownView.didSelectItem = (item) => {\n this.commitSelection(item)\n }\n \n let textBeforeFocus = this.text\n let itemBeforeFocus = this.selectedItem\n \n // Open dropdown on focus.\n // If a selection is already committed we keep the confirmed text visible\n // and do not clear it \u2014 this covers Tab-into-field after a prior selection,\n // as well as returning focus after commitSelection.\n this.controlEventTargetAccumulator.Focus = () => {\n textBeforeFocus = this.text\n itemBeforeFocus = this.selectedItem\n this._userHasNavigatedDropdown = NO\n this.openDropdown()\n this.textElementView.viewHTMLElement.select()\n const matchIndex = this._dropdownView.filteredItems.findIndex(\n item => item.label === textBeforeFocus\n )\n if (matchIndex !== -1) {\n this._dropdownView.highlightedRowIndex = matchIndex\n }\n }\n \n // Close on blur \u2014 but not when focus is moving into the dropdown itself\n // (e.g. the user clicked a row). relatedTarget is the element receiving\n // focus, so if it is inside the dropdown's element tree, keep it open and\n // let the row's PointerUpInside / didSelectItem complete the selection.\n this.controlEventTargetAccumulator.Blur = (sender, event) => {\n const focusEvent = event as FocusEvent\n if (\n IS(focusEvent.relatedTarget) &&\n this._dropdownView.viewHTMLElement.contains(focusEvent.relatedTarget as Node)\n ) {\n return\n }\n this.closeDropdown()\n }\n \n // Filter on text change\n this.addTargetForControlEvent(UITextField.controlEvent.TextChange, () => {\n this._selectedItem = undefined\n this._userHasNavigatedDropdown = this.text.length > 0\n this.updateFilteredItems()\n if (!this._isDropdownOpen) {\n this.openDropdown()\n }\n })\n \n // Keyboard navigation: down arrow\n this.textElementView.addTargetForControlEvent(UIView.controlEvent.DownArrowDown, (sender, event) => {\n event.preventDefault()\n if (!this._isDropdownOpen) {\n this.openDropdown()\n return\n }\n this._userHasNavigatedDropdown = YES\n const maxIndex = this._dropdownView.filteredItems.length - 1\n if (this._dropdownView.highlightedRowIndex < maxIndex) {\n this._dropdownView.highlightedRowIndex = this._dropdownView.highlightedRowIndex + 1\n }\n })\n \n // Keyboard navigation: up arrow\n this.textElementView.addTargetForControlEvent(UIView.controlEvent.UpArrowDown, (sender, event) => {\n event.preventDefault()\n this._userHasNavigatedDropdown = YES\n if (this._dropdownView.highlightedRowIndex > 0) {\n this._dropdownView.highlightedRowIndex = this._dropdownView.highlightedRowIndex - 1\n }\n })\n \n // Enter: commit focused item\n this.addTargetForControlEvent(UIView.controlEvent.EnterDown, () => {\n const highlightedItem = this._dropdownView.highlightedItem\n if (IS(highlightedItem)) {\n this.commitSelection(highlightedItem)\n }\n else if (this._isDropdownOpen) {\n this.closeDropdown()\n }\n })\n \n // Tab: commit highlighted item only if the user has explicitly interacted\n // with the dropdown (typed or used arrow keys). This prevents tabbing\n // through the form from silently committing the first suggestion.\n // When the user has not navigated, we do NOT consume the event \u2014 the\n // dropdown will close via the Blur handler and RHPageFocusManager will\n // handle the Tab navigation normally.\n this.addTargetForControlEvent(UIView.controlEvent.TabDown, (sender, event) => {\n if (this._isDropdownOpen && this._userHasNavigatedDropdown) {\n const highlightedItem = this._dropdownView.highlightedItem\n if (IS(highlightedItem)) {\n event.preventDefault()\n this.commitSelection(highlightedItem)\n }\n }\n })\n \n // Escape: dismiss dropdown\n this.addTargetForControlEvent(UIView.controlEvent.EscDown, () => {\n if (this._isDropdownOpen) {\n this.closeDropdown()\n if (this.strictSelection) {\n this.commitSelection(itemBeforeFocus as any)\n }\n else {\n this.text = textBeforeFocus\n }\n }\n })\n \n }\n \n \n /** Override in subclass to provide a custom dropdown view. */\n newDropdownView(): UIAutocompleteDropdownView<T> {\n return new UIAutocompleteDropdownView<T>(\n this.elementID ? this.elementID + \"Dropdown\" : undefined\n )\n }\n \n \n // MARK: - Selection\n \n get selectedItem(): T | undefined {\n return this._selectedItem?.value\n }\n \n \n get strictSelection(): boolean {\n return this._strictSelection\n }\n \n set strictSelection(strict: boolean) {\n this._strictSelection = strict\n this.updateValidationVisuals()\n }\n \n \n commitSelection(item: UIAutocompleteItem<T>) {\n \n // Set the selection state and close the dropdown without blurring.\n // Keeping focus on this view means the next Tab press is handled by\n // our TabDown handler rather than being picked up natively by the browser.\n this._selectedItem = item\n this.text = item.label\n this.closeDropdown()\n this.updateValidationVisuals()\n this.sendControlEventForKey(UIAutocompleteTextField.controlEvent.SelectionDidChange)\n \n }\n \n \n // MARK: - Data\n \n /** Convenience: set string suggestions. Each string becomes { label: s, value: s }. */\n set autocompleteStrings(strings: string[]) {\n this._autocompleteItems = strings.map(s => ({\n label: s,\n value: s as unknown as T\n }))\n this.updateFilteredItems()\n }\n \n get autocompleteStrings(): string[] {\n return this._autocompleteItems.map(item => item.label)\n }\n \n set autocompleteData(items: UIAutocompleteItem<T>[]) {\n this._autocompleteItems = items\n this.updateFilteredItems()\n }\n \n get autocompleteData(): UIAutocompleteItem<T>[] {\n return this._autocompleteItems\n }\n \n \n // MARK: - Filtering\n \n /**\n * Splits the given lowercase-trimmed filter text into individual words when\n * usesMultiWordAndSearch is YES, or returns it as a single-element array otherwise.\n * Returns an empty array when the input is empty.\n */\n _filterWordsFromText(filterText: string): string[] {\n if (filterText.length === 0) {\n return []\n }\n if (this.usesMultiWordAndSearch) {\n return filterText.split(/\\s+/).filter(word => word.length > 0)\n }\n return [filterText]\n }\n \n \n /**\n * Returns true when the given label (already lowercased) satisfies all filter\n * words \u2014 i.e. every word appears somewhere in the label.\n */\n _labelMatchesFilterWords(label: string, filterWords: string[]): boolean {\n return filterWords.every(word => label.includes(word))\n }\n \n \n /**\n * Returns true when the character immediately before `position` in `label`\n * is a word separator (or the position is at the start of the string).\n * Used to give a bonus to matches that start at a word boundary.\n */\n _isWordBoundary(label: string, position: number): boolean {\n if (position === 0) {\n return YES\n }\n const charBefore = label[position - 1]\n return \" -/\\\\|._,;:()[]\".includes(charBefore)\n }\n \n \n /**\n * Scores a label against the filter words. Lower score = better match.\n *\n * Scoring factors (in priority order):\n * 1. Non-sequential penalty \u2014 words must appear in typed order to avoid\n * a large penalty that pushes them below all sequential matches.\n * 2. Per-word boundary score \u2014 for each filter word, a mid-word match\n * scores worse than a word-boundary match. The sum across all words\n * determines the boundary tier.\n * 3. Position of the first matched word \u2014 within the same boundary tier,\n * earlier appearances rank higher.\n * 4. Total label length \u2014 shorter labels are more specific (tiebreaker).\n *\n * Example: query \"p\u00F5 pu\"\n * \"P\u00F5hjavee puhastusvahendid\" \u2192 \"p\u00F5\" at boundary(0), \"pu\" at boundary(9) \u2192 low boundary score\n * \"p\u00F5randapuhastusvahendid\" \u2192 \"p\u00F5\" at boundary(0), \"pu\" mid-word(7) \u2192 higher boundary score\n * \u2192 \"P\u00F5hjavee puhastusvahendid\" ranks first.\n */\n _scoreLabel(label: string, filterWords: string[]): number {\n \n if (filterWords.length === 0) {\n return label.length\n }\n \n // --- Sequential check ---\n // Scan left-to-right; if all words appear in order record the positions.\n let cursor = 0\n let isSequential = YES\n const sequentialPositions: number[] = []\n for (const word of filterWords) {\n const position = label.indexOf(word, cursor)\n if (position === -1) {\n isSequential = NO\n break\n }\n sequentialPositions.push(position)\n cursor = position + word.length\n }\n \n // --- Boundary score ---\n // For each filter word find its best (leftmost) match and check whether\n // it lands on a word boundary. Non-boundary matches incur a per-word\n // penalty of 1, so the boundary score is 0..filterWords.length.\n let boundaryScore = 0\n for (const word of filterWords) {\n const position = label.indexOf(word)\n if (position !== -1 && !this._isWordBoundary(label, position)) {\n boundaryScore += 1\n }\n }\n \n // Position of the first word's earliest match.\n const firstMatchPosition = label.indexOf(filterWords[0])\n \n // Compose score \u2014 each tier must not overflow into the next:\n // Non-sequential penalty : 10 000 000 (dominates everything)\n // Boundary score : 10 000 (per word, max ~10 words \u2192 100 000 max, safe)\n // First-match position : 100 (labels rarely exceed 200 chars)\n // Label length : 1 (tiebreaker)\n const sequentialPenalty = isSequential ? 0 : 10_000_000\n \n return sequentialPenalty +\n boundaryScore * 10_000 +\n firstMatchPosition * 100 +\n label.length\n \n }\n \n \n updateFilteredItems() {\n \n const rawFilterText = this.text.toLowerCase().trim()\n const filterWords = this._filterWordsFromText(rawFilterText)\n \n let filtered: UIAutocompleteItem<T>[]\n \n if (filterWords.length === 0) {\n filtered = this._autocompleteItems\n }\n else {\n filtered = this._autocompleteItems\n .filter(item => this._labelMatchesFilterWords(item.label.toLowerCase(), filterWords))\n .map((item, originalIndex) => ({ item, originalIndex, score: this._scoreLabel(item.label.toLowerCase(), filterWords) }))\n .sort((a, b) => a.score - b.score || a.originalIndex - b.originalIndex)\n .map(({ item }) => item)\n }\n \n // If the only remaining result is an exact match for the current text,\n // the user has already made their selection \u2014 no need to show the dropdown.\n const isExactSingleMatch = filtered.length === 1 &&\n filtered[0].label.toLowerCase() === rawFilterText\n \n this._dropdownView.filterWords = filterWords\n this._dropdownView.filteredItems = isExactSingleMatch ? [] : filtered\n \n if (this._dropdownView.filteredItems.length > 0) {\n this._dropdownView.highlightedRowIndex = 0\n }\n \n if (this._isDropdownOpen) {\n this._dropdownView.showAnchoredToView(this)\n }\n \n }\n \n \n // MARK: - Dropdown Lifecycle\n \n openDropdown() {\n \n if (this._isDropdownOpen) {\n return\n }\n \n this._isDropdownOpen = YES\n this._dropdownView.filterWords = []\n this._dropdownView.filteredItems = this._autocompleteItems\n if (this._dropdownView.filteredItems.length > 0) {\n const matchIndex = this._autocompleteItems.findIndex(\n item => item.label === this.text\n )\n this._dropdownView.highlightedRowIndex = matchIndex !== -1 ? matchIndex : 0\n }\n this._dropdownView.showAnchoredToView(this)\n \n }\n \n closeDropdown() {\n \n if (!this._isDropdownOpen) {\n return\n }\n \n this._isDropdownOpen = NO\n this._dropdownView.dismiss()\n \n // In strict mode, clear text if it doesn't match any item\n if (this._strictSelection && IS_NOT(this._selectedItem)) {\n const currentText = this.text.trim()\n if (currentText.length > 0) {\n const matchingItem = this._autocompleteItems.find(\n item => item.label === currentText\n )\n if (IS(matchingItem)) {\n this._selectedItem = matchingItem\n }\n else {\n this.text = \"\"\n this._selectedItem = undefined\n this.sendControlEventForKey(UITextField.controlEvent.TextChange)\n }\n }\n }\n \n this.updateValidationVisuals()\n \n }\n \n \n // MARK: - Validation\n \n /** Whether the current text is valid given the strictSelection setting. */\n get isValid(): boolean {\n \n if (!this._strictSelection) {\n return YES\n }\n \n const currentText = this.text.trim()\n \n if (currentText.length === 0) {\n return YES\n }\n \n return this._autocompleteItems.some(item => item.label === currentText)\n \n }\n \n \n /**\n * Hook for subclasses to apply custom visual styling based on validation state.\n * Called after dropdown closes and after selection changes.\n */\n updateValidationVisuals() {\n // Base implementation does nothing. Subclasses override.\n }\n \n \n // MARK: - Cleanup\n \n override wasRemovedFromViewTree() {\n \n super.wasRemovedFromViewTree()\n \n this._dropdownView.removeFromSuperview()\n \n }\n \n \n // MARK: - Layout\n \n override layoutSubviews() {\n \n super.layoutSubviews()\n \n if (this._isDropdownOpen) {\n this._dropdownView.showAnchoredToView(this)\n }\n \n }\n \n \n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -675,8 +675,8 @@ class UITableView extends import_UINativeScrollView.UINativeScrollView {
675
675
  while (walkedTarget && walkedTarget !== el) {
676
676
  const viewObject = walkedTarget.UIViewObject;
677
677
  if ((viewObject == null ? void 0 : viewObject._UITableViewRowIndex) !== void 0) {
678
- el.focus({ preventScroll: true });
679
678
  this._setKeyboardFocus(viewObject._UITableViewRowIndex, this._keyboardFocusedCellIndex);
679
+ el.focus({ preventScroll: true });
680
680
  return;
681
681
  }
682
682
  walkedTarget = walkedTarget.parentElement;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../scripts/UITableView.ts"],
4
- "sourcesContent": ["import { UIButton } from \"./UIButton\"\nimport { UINativeScrollView } from \"./UINativeScrollView\"\nimport { EXTEND, FIRST_OR_NIL, IF, IS, IS_DEFINED, MAKE_ID, nil, NO, YES } from \"./UIObject\"\nimport { UIPoint } from \"./UIPoint\"\nimport { UIRectangle } from \"./UIRectangle\"\nimport { UIView, UIViewBroadcastEvent } from \"./UIView\"\n\n\ninterface UITableViewRowView extends UIView {\n \n _UITableViewRowIndex?: number;\n \n}\n\n\nexport interface UITableViewReusableViewsContainerObject {\n \n [key: string]: UIView[];\n \n}\n\n\nexport interface UITableViewReusableViewPositionObject {\n \n bottomY: number;\n topY: number;\n \n isValid: boolean;\n \n}\n\n\nexport class UITableView extends UINativeScrollView {\n \n \n allRowsHaveEqualHeight: boolean = NO\n _visibleRows: UITableViewRowView[] = []\n \n /** Shared intrinsic size cache identifier used for all row views when\n * allRowsHaveEqualHeight is YES. Stable for the lifetime of the table;\n * the shared cache bucket is invalidated on reloadData and\n * clearIntrinsicSizeCache so the height is re-measured after data changes. */\n _equalRowHeightCacheIdentifier: string\n \n _rowPositions: UITableViewReusableViewPositionObject[] = []\n \n _highestValidRowPositionIndex: number = 0\n \n _unusedReusableViews: UITableViewReusableViewsContainerObject = {}\n \n _fullHeightView: UIView\n _rowIDIndex: number = 0\n reloadsOnLanguageChange = YES\n sidePadding = 0\n \n cellWeights?: number[]\n \n _persistedData: any[] = []\n _needsDrawingOfVisibleRowsBeforeLayout = NO\n _isDrawVisibleRowsScheduled = NO\n _shouldAnimateNextLayout?: boolean\n \n override usesVirtualLayoutingForIntrinsicSizing = NO\n \n override animationDuration = 0.25\n \n // Viewport scrolling properties\n _intersectionObserver?: IntersectionObserver\n \n // -------------------------------------------------------------------------\n // Keyboard navigation state\n // -------------------------------------------------------------------------\n \n /** Row index with -1 meaning the header row. undefined means no focus. */\n _keyboardFocusedRowIndex: number | undefined = undefined\n /** Cell index within the focused row. */\n _keyboardFocusedCellIndex: number = 0\n /** Total number of data columns (excludes left/right side cells). Set by CBDataView. */\n _columnCount: number = 0\n /** Called by UITableView when the focused row/cell changes. CBDataView overrides this. */\n keyboardFocusDidChange?: (rowIndex: number | undefined, cellIndex: number) => void\n /** Fired when Enter is pressed on a focused cell. Passes rowIndex and cellIndex. */\n keyboardDidActivateCell?: (rowIndex: number, cellIndex: number) => void\n \n _keydownHandler?: (event: KeyboardEvent) => void\n _keyboardListenersAttached = false\n \n \n get _reusableViews(): UITableViewReusableViewsContainerObject {\n \n const result: UITableViewReusableViewsContainerObject = {}\n \n const addView = (view: UIView) => {\n const identifier = view._UITableViewReusabilityIdentifier\n if (!identifier) {\n return\n }\n if (!result[identifier]) {\n result[identifier] = []\n }\n result[identifier].push(view)\n }\n \n this._visibleRows.forEach(addView)\n \n this._unusedReusableViews.forEach((views: UIView[]) => views.forEach(addView))\n \n return result\n \n }\n \n \n constructor(elementID?: string) {\n \n super(elementID)\n \n this._equalRowHeightCacheIdentifier = (elementID ?? MAKE_ID()) + \"_rowHeight\"\n \n this._fullHeightView = new UIView()\n this._fullHeightView.hidden = YES\n this._fullHeightView.userInteractionEnabled = NO\n this.addSubview(this._fullHeightView)\n \n this.scrollsX = NO\n \n this._setupViewportScrollAndResizeHandlersIfNeeded()\n this._setupGridAccessibility()\n this._setupKeyboardNavigation()\n \n }\n \n \n // -------------------------------------------------------------------------\n // ARIA / Accessibility setup\n // -------------------------------------------------------------------------\n \n /**\n * The element that receives tabIndex, ARIA grid role, and all keyboard/pointer\n * listeners. Defaults to the table's own element. CBDataView overrides this\n * to a container that wraps both the header and the table, so the focus ring\n * encompasses both.\n */\n _keyboardListenerElement: HTMLElement = this.viewHTMLElement\n \n _setupGridAccessibility() {\n const el = this._keyboardListenerElement\n el.setAttribute(\"role\", \"grid\")\n el.setAttribute(\"aria-rowcount\", \"0\")\n el.setAttribute(\"aria-colcount\", \"0\")\n el.tabIndex = 0\n }\n \n /** Called by CBDataView after descriptors change. */\n setColumnCount(count: number) {\n this._columnCount = count\n this._keyboardListenerElement.setAttribute(\"aria-colcount\", String(count))\n }\n \n /** Called by CBDataView after data loads. */\n setRowCount(count: number) {\n this._keyboardListenerElement.setAttribute(\"aria-rowcount\", String(count))\n }\n \n \n // -------------------------------------------------------------------------\n // Keyboard navigation\n // -------------------------------------------------------------------------\n \n _setupKeyboardNavigation() {\n \n this._keydownHandler = (event: KeyboardEvent) => {\n \n if (!this.isMemberOfViewTree) {\n return\n }\n \n const target = event.target as HTMLElement\n if (target.tagName === \"INPUT\" || target.tagName === \"TEXTAREA\") {\n return\n }\n \n const rowCount = this.numberOfRows()\n const hasHeader = this._keyboardFocusedRowIndex !== undefined\n \n if (event.key === \"ArrowDown\") {\n event.preventDefault()\n if (this._keyboardFocusedRowIndex === undefined) {\n this._setKeyboardFocus(0, this._keyboardFocusedCellIndex)\n }\n else if (event.metaKey || event.ctrlKey) {\n this._setKeyboardFocus(rowCount - 1, this._keyboardFocusedCellIndex)\n }\n else if (event.altKey) {\n const pageSize = Math.max(1, Math.floor(this.bounds.height / (this._heightForAnyRow() || 50)))\n const next = Math.min(\n (this._keyboardFocusedRowIndex < 0 ? 0 : this._keyboardFocusedRowIndex) + pageSize,\n rowCount - 1\n )\n this._setKeyboardFocus(next, this._keyboardFocusedCellIndex)\n }\n else if (this._keyboardFocusedRowIndex === -1) {\n this._setKeyboardFocus(0, this._keyboardFocusedCellIndex)\n }\n else if (this._keyboardFocusedRowIndex < rowCount - 1) {\n this._setKeyboardFocus(this._keyboardFocusedRowIndex + 1, this._keyboardFocusedCellIndex)\n }\n }\n else if (event.key === \"ArrowUp\") {\n event.preventDefault()\n if (this._keyboardFocusedRowIndex === undefined) {\n this._setKeyboardFocus(rowCount - 1, this._keyboardFocusedCellIndex)\n }\n else if (event.metaKey || event.ctrlKey) {\n this._setKeyboardFocus(-1, this._keyboardFocusedCellIndex)\n }\n else if (event.altKey) {\n const pageSize = Math.max(1, Math.floor(this.bounds.height / (this._heightForAnyRow() || 50)))\n const prev = Math.max(\n (this._keyboardFocusedRowIndex < 0 ? 0 : this._keyboardFocusedRowIndex) - pageSize, -1)\n this._setKeyboardFocus(prev, this._keyboardFocusedCellIndex)\n }\n else if (this._keyboardFocusedRowIndex === 0) {\n this._setKeyboardFocus(-1, this._keyboardFocusedCellIndex)\n }\n else if (this._keyboardFocusedRowIndex > 0) {\n this._setKeyboardFocus(this._keyboardFocusedRowIndex - 1, this._keyboardFocusedCellIndex)\n }\n }\n else if (event.key === \"ArrowRight\") {\n event.preventDefault()\n if (this._keyboardFocusedRowIndex !== undefined && this._columnCount > 0) {\n const nextCell = event.metaKey || event.ctrlKey\n ? this._columnCount - 1\n : Math.min(this._keyboardFocusedCellIndex + 1, this._columnCount - 1)\n this._setKeyboardFocus(this._keyboardFocusedRowIndex, nextCell)\n }\n }\n else if (event.key === \"ArrowLeft\") {\n event.preventDefault()\n if (this._keyboardFocusedRowIndex !== undefined && this._columnCount > 0) {\n const prevCell = event.metaKey || event.ctrlKey\n ? 0\n : Math.max(this._keyboardFocusedCellIndex - 1, 0)\n this._setKeyboardFocus(this._keyboardFocusedRowIndex, prevCell)\n }\n }\n else if (event.key === \"Home\") {\n event.preventDefault()\n if (this._keyboardFocusedRowIndex !== undefined) {\n this._setKeyboardFocus(this._keyboardFocusedRowIndex, 0)\n }\n }\n else if (event.key === \"End\") {\n event.preventDefault()\n if (this._keyboardFocusedRowIndex !== undefined && this._columnCount > 0) {\n this._setKeyboardFocus(this._keyboardFocusedRowIndex, this._columnCount - 1)\n }\n }\n else if (event.key === \"PageDown\") {\n event.preventDefault()\n if (this._keyboardFocusedRowIndex !== undefined) {\n const pageSize = Math.max(1, Math.floor(this.bounds.height / (this._heightForAnyRow() || 50)))\n const next = Math.min(\n (this._keyboardFocusedRowIndex < 0 ? 0 : this._keyboardFocusedRowIndex) + pageSize,\n rowCount - 1\n )\n this._setKeyboardFocus(next, this._keyboardFocusedCellIndex)\n }\n }\n else if (event.key === \"PageUp\") {\n event.preventDefault()\n if (this._keyboardFocusedRowIndex !== undefined) {\n const pageSize = Math.max(1, Math.floor(this.bounds.height / (this._heightForAnyRow() || 50)))\n const prev = Math.max(\n (this._keyboardFocusedRowIndex < 0 ? 0 : this._keyboardFocusedRowIndex) - pageSize, -1)\n this._setKeyboardFocus(prev, this._keyboardFocusedCellIndex)\n }\n }\n else if (event.key === \"Enter\" || event.key === \" \") {\n if (this._keyboardFocusedRowIndex !== undefined && this._keyboardFocusedRowIndex >= 0) {\n event.preventDefault()\n this.keyboardDidActivateCell?.(this._keyboardFocusedRowIndex, this._keyboardFocusedCellIndex)\n }\n }\n else if (event.key === \"Escape\") {\n // Release focus from the table \u2014 move to next focusable element\n this._clearKeyboardFocus()\n this._keyboardListenerElement.blur()\n }\n \n }\n \n // Listeners are attached in wasAddedToViewTree to guarantee they land\n // on the final stable viewHTMLElement after the framework has fully\n // initialised the view.\n \n }\n \n /**\n * Move keyboard focus to a specific row and cell.\n * rowIndex = -1 means the header row.\n */\n _setKeyboardFocus(rowIndex: number, cellIndex: number) {\n \n const previousRowIndex = this._keyboardFocusedRowIndex\n const previousCellIndex = this._keyboardFocusedCellIndex\n \n // When moving to a different data row, land on the first button cell by default\n if (rowIndex >= 0 && rowIndex !== previousRowIndex) {\n const row = this.visibleRowWithIndex(rowIndex) as any\n if (row && typeof row.firstButtonCellIndex === \"function\") {\n cellIndex = row.firstButtonCellIndex()\n }\n }\n \n this._keyboardFocusedRowIndex = rowIndex\n this._keyboardFocusedCellIndex = cellIndex\n \n // Clear highlight from old position\n if (previousRowIndex !== undefined && previousRowIndex !== rowIndex) {\n this._clearKeyboardFocusOnRow(previousRowIndex)\n }\n else if (previousRowIndex === rowIndex && previousCellIndex !== cellIndex) {\n this._clearKeyboardFocusOnRow(rowIndex)\n }\n \n // Scroll the focused row into view if it is a data row\n if (rowIndex >= 0) {\n this._scrollRowIntoView(rowIndex)\n }\n \n // Apply highlight to new position\n this._applyKeyboardFocusToVisibleRows()\n \n // Notify observers (CBDataView uses this to sync header highlight)\n this.keyboardFocusDidChange?.(rowIndex, cellIndex)\n \n }\n \n _clearKeyboardFocus() {\n const previous = this._keyboardFocusedRowIndex\n this._keyboardFocusedRowIndex = undefined\n if (previous !== undefined) {\n this._clearKeyboardFocusOnRow(previous)\n }\n this.keyboardFocusDidChange?.(undefined, this._keyboardFocusedCellIndex)\n }\n \n _clearKeyboardFocusOnRow(rowIndex: number) {\n if (rowIndex === -1) {\n // Header \u2014 notify via callback; CBDataView handles the header view\n this.keyboardFocusDidChange?.(-1, -1)\n return\n }\n const row = this.visibleRowWithIndex(rowIndex) as any\n if (row && typeof row.setKeyboardFocusedCellIndex === \"function\") {\n row.setKeyboardFocusedCellIndex(undefined)\n }\n }\n \n _applyKeyboardFocusToVisibleRows(clearAll = false) {\n this._visibleRows.forEach((row: any) => {\n if (typeof row.setKeyboardFocusedCellIndex !== \"function\") {\n return\n }\n if (clearAll || row._UITableViewRowIndex !== this._keyboardFocusedRowIndex) {\n row.setKeyboardFocusedCellIndex(undefined)\n }\n else {\n row.setKeyboardFocusedCellIndex(this._keyboardFocusedCellIndex)\n }\n })\n }\n \n _scrollRowIntoView(rowIndex: number) {\n const position = this._rowPositionWithIndex(rowIndex)\n if (!position) {\n return\n }\n const offsetY = this.contentOffset.y\n const visibleHeight = this.bounds.height\n if (position.topY < offsetY) {\n const duration = this.animationDuration\n this.animationDuration = 0\n this.contentOffset = this.contentOffset.pointWithY(position.topY)\n this.animationDuration = duration\n }\n else if (position.bottomY > offsetY + visibleHeight) {\n const duration = this.animationDuration\n this.animationDuration = 0\n this.contentOffset = this.contentOffset.pointWithY(position.bottomY - visibleHeight)\n this.animationDuration = duration\n }\n }\n \n /** Expose so CBDataView can call it after loading data. */\n focusRowAtIndex(rowIndex: number, cellIndex: number = 0) {\n this._setKeyboardFocus(rowIndex, cellIndex)\n this._keyboardListenerElement.focus({ preventScroll: true })\n }\n \n \n _windowScrollHandler = () => {\n if (!this.isMemberOfViewTree) {\n return\n }\n this._scheduleDrawVisibleRows()\n }\n \n _resizeHandler = () => {\n if (!this.isMemberOfViewTree) {\n return\n }\n // Invalidate all row positions on resize as widths may have changed\n this._rowPositions.everyElement.isValid = NO\n this._highestValidRowPositionIndex = -1\n this._scheduleDrawVisibleRows()\n }\n \n _setupViewportScrollAndResizeHandlersIfNeeded() {\n if (this._intersectionObserver) {\n return\n }\n \n window.addEventListener(\"scroll\", this._windowScrollHandler, { passive: true })\n window.addEventListener(\"resize\", this._resizeHandler, { passive: true })\n \n // Use IntersectionObserver to detect when table enters/exits viewport\n this._intersectionObserver = new IntersectionObserver(\n (entries) => {\n entries.forEach(entry => {\n if (entry.isIntersecting && this.isMemberOfViewTree) {\n this._scheduleDrawVisibleRows()\n }\n })\n },\n {\n root: null,\n rootMargin: \"100% 0px\", // Load rows 100% viewport height before/after\n threshold: 0\n }\n )\n this._intersectionObserver.observe(this.viewHTMLElement)\n }\n \n \n _cleanupViewportScrollListeners() {\n window.removeEventListener(\"scroll\", this._windowScrollHandler)\n window.removeEventListener(\"resize\", this._resizeHandler)\n if (this._intersectionObserver) {\n this._intersectionObserver.disconnect()\n this._intersectionObserver = undefined\n }\n }\n \n override wasRemovedFromViewTree() {\n super.wasRemovedFromViewTree()\n this._cleanupViewportScrollListeners()\n if (this._keydownHandler) {\n this.viewHTMLElement.removeEventListener(\"keydown\", this._keydownHandler)\n }\n // Reset so listeners are re-attached if added back to the tree\n this._keyboardListenersAttached = false\n }\n \n \n loadData() {\n \n this._persistedData = []\n \n this._calculatePositionsUntilIndex(this.numberOfRows() - 1)\n this._needsDrawingOfVisibleRowsBeforeLayout = YES\n \n this.setNeedsLayout()\n \n }\n \n reloadData() {\n \n this._removeVisibleRows()\n this._removeAllReusableRows()\n \n this._rowPositions = []\n this._highestValidRowPositionIndex = -1\n \n if (this.allRowsHaveEqualHeight) {\n UIView.invalidateSharedIntrinsicSizeCache(this._equalRowHeightCacheIdentifier)\n }\n \n this.loadData()\n \n }\n \n \n highlightChanges(previousData: any[], newData: any[]) {\n \n previousData = previousData.map(dataPoint => JSON.stringify(dataPoint))\n newData = newData.map(dataPoint => JSON.stringify(dataPoint))\n \n const newIndexes: number[] = []\n \n newData.forEach((value, index) => {\n \n if (!previousData.contains(value)) {\n \n newIndexes.push(index)\n \n }\n \n })\n \n newIndexes.forEach(index => {\n \n if (this.isRowWithIndexVisible(index)) {\n this.highlightRowAsNew(\n this.visibleRowWithIndex(index) ?? this.viewForRowWithIndex(index)\n )\n }\n \n })\n \n }\n \n \n highlightRowAsNew(row: UIView) {\n \n \n }\n \n \n invalidateSizeOfRowWithIndex(index: number, animateChange = NO) {\n if (this._rowPositions?.[index]) {\n FIRST_OR_NIL(this._rowPositions[index]).isValid = NO\n this._rowPositions.slice(index).everyElement.isValid = NO\n }\n this._highestValidRowPositionIndex = Math.min(this._highestValidRowPositionIndex, index - 1)\n this._needsDrawingOfVisibleRowsBeforeLayout = YES\n this._shouldAnimateNextLayout = animateChange\n }\n \n \n _rowPositionWithIndex(index: number, positions = this._rowPositions) {\n if (this.allRowsHaveEqualHeight && index > 0) {\n const firstPositionObject = positions[0]\n const rowHeight = firstPositionObject.bottomY - firstPositionObject.topY\n const result = {\n bottomY: rowHeight * (index + 1),\n topY: rowHeight * index,\n isValid: firstPositionObject.isValid\n }\n return result\n }\n return positions[index]\n }\n \n _calculateAllPositions() {\n this._calculatePositionsUntilIndex(this.numberOfRows() - 1)\n }\n \n _calculatePositionsUntilIndex(maxIndex: number) {\n \n if (this.allRowsHaveEqualHeight) {\n const positionObject: UITableViewReusableViewPositionObject = {\n bottomY: this._heightForAnyRow(),\n topY: 0,\n isValid: YES\n }\n this._rowPositions = [positionObject]\n return\n }\n \n let validPositionObject = this._rowPositions[this._highestValidRowPositionIndex]\n if (!IS(validPositionObject)) {\n validPositionObject = {\n bottomY: 0,\n topY: 0,\n isValid: YES\n }\n }\n \n let previousBottomY = validPositionObject.bottomY\n if (!this._rowPositions.length) {\n this._highestValidRowPositionIndex = -1\n }\n \n for (let i = this._highestValidRowPositionIndex + 1; i <= maxIndex; i++) {\n \n let height: number\n \n const rowPositionObject = this._rowPositions[i]\n \n if (IS((rowPositionObject || nil).isValid)) {\n height = rowPositionObject.bottomY - rowPositionObject.topY\n }\n // Do not calculate heights if all rows have equal heights, and we already have a height\n else if (this.allRowsHaveEqualHeight && i > 0) {\n height = this._rowPositions[0].bottomY - this._rowPositions[0].topY\n }\n else {\n height = this.heightForRowWithIndex(i)\n }\n \n const positionObject: UITableViewReusableViewPositionObject = {\n bottomY: previousBottomY + height,\n topY: previousBottomY,\n isValid: YES\n }\n \n if (i < this._rowPositions.length) {\n this._rowPositions[i] = positionObject\n }\n else {\n this._rowPositions.push(positionObject)\n }\n this._highestValidRowPositionIndex = i\n previousBottomY = previousBottomY + height\n \n }\n \n }\n \n \n _heightForAnyRow(calculateVisibleRows = YES) {\n return this.heightForRowWithIndex(\n this._visibleRows.firstElement?._UITableViewRowIndex ??\n (calculateVisibleRows ? this.indexesForVisibleRows().firstElement : 0) ??\n 0\n )\n }\n \n indexesForVisibleRows(paddingRatio = 0.5): number[] {\n \n // 1. Calculate the visible frame relative to the Table's bounds (0,0 is top-left of the table view)\n // This accounts for the Window viewport clipping the table if it is partially off-screen.\n const tableRect = this.viewHTMLElement.getBoundingClientRect()\n const viewportHeight = window.innerHeight\n const pageScale = UIView.pageScale\n \n // The top of the visible window relative to the view's top edge.\n // If tableRect.top is negative, the table is scrolled up and clipped by the window top.\n const visibleFrameTop = Math.max(0, -tableRect.top / pageScale)\n \n // The bottom of the visible window relative to the view's top edge.\n // We clip it to the table's actual bounds height so we don't look past the table content.\n const visibleFrameBottom = Math.min(\n this.bounds.height,\n (viewportHeight - tableRect.top) / pageScale\n )\n \n // If the table is completely off-screen, return empty\n if (visibleFrameBottom <= visibleFrameTop) {\n return []\n }\n \n // 2. Convert to Content Coordinates (Scroll Offset)\n // contentOffset.y is the internal scroll position.\n // If using viewport scrolling (full height), contentOffset.y is typically 0.\n // If using internal scrolling, this shifts the visible frame to the correct content rows.\n let firstVisibleY = this.contentOffset.y + visibleFrameTop\n let lastVisibleY = this.contentOffset.y + visibleFrameBottom\n \n // 3. Apply Padding\n // We calculate padding based on the viewport height to ensure smooth scrolling\n const paddingPx = (viewportHeight / pageScale) * paddingRatio\n firstVisibleY = Math.max(0, firstVisibleY - paddingPx)\n lastVisibleY = lastVisibleY + paddingPx\n \n const numberOfRows = this.numberOfRows()\n \n // 4. Find Indexes\n if (this.allRowsHaveEqualHeight) {\n \n const rowHeight = this._heightForAnyRow(NO)\n \n let firstIndex = Math.floor(firstVisibleY / rowHeight)\n let lastIndex = Math.floor(lastVisibleY / rowHeight)\n \n // Clamp BOTH indexes to [0, numberOfRows-1].\n // Without the upper clamp on firstIndex, when the viewport extends below the\n // last row firstIndex can exceed numberOfRows-1 while lastIndex is already\n // clamped there. firstIndex > lastIndex \u2192 empty result \u2192 _removeVisibleRows \u2192\n // browser collapses scrollHeight \u2192 scrollTop resets to 0 \u2192 rows pile at top.\n firstIndex = Math.max(0, Math.min(firstIndex, numberOfRows - 1))\n lastIndex = Math.max(0, Math.min(lastIndex, numberOfRows - 1))\n \n const result = []\n for (let i = firstIndex; i <= lastIndex; i++) {\n result.push(i)\n }\n return result\n }\n \n // Variable Heights\n this._calculateAllPositions()\n const result = []\n \n // Clamp firstVisibleY to the actual content height so that when the viewport\n // extends below the last row the intersection check still matches the final rows\n // rather than producing an empty result.\n const totalContentHeight = IF(this._rowPositions.lastElement)(() =>\n this._rowPositions.lastElement.bottomY\n ).ELSE(() =>\n 0\n )\n firstVisibleY = Math.min(firstVisibleY, totalContentHeight)\n \n for (let i = 0; i < numberOfRows; i++) {\n \n const position = this._rowPositionWithIndex(i)\n if (!position) {\n break\n }\n \n const rowTop = position.topY\n const rowBottom = position.bottomY\n \n // Check intersection\n if (rowBottom >= firstVisibleY && rowTop <= lastVisibleY) {\n result.push(i)\n }\n \n if (rowTop > lastVisibleY) {\n break\n }\n \n }\n \n return result\n \n }\n \n \n // This is called when no rows are supposed to be visible as a performance shortcut\n _removeVisibleRows() {\n \n this._visibleRows.forEach((row: UIView) => {\n \n this._persistedData[row._UITableViewRowIndex as number] = this.persistenceDataItemForRowWithIndex(\n row._UITableViewRowIndex as number,\n row\n )\n row.removeFromSuperview()\n this._markReusableViewAsUnused(row)\n \n })\n this._visibleRows = []\n \n }\n \n \n _removeAllReusableRows() {\n this._unusedReusableViews.forEach((rows: UIView[]) =>\n rows.forEach((row: UIView) => {\n \n this._persistedData[row._UITableViewRowIndex as number] = this.persistenceDataItemForRowWithIndex(\n row._UITableViewRowIndex as number,\n row\n )\n row.removeFromSuperview()\n \n })\n )\n this._unusedReusableViews = {}\n }\n \n \n _markReusableViewAsUnused(row: UIView) {\n const identifier = row._UITableViewReusabilityIdentifier\n if (!this._unusedReusableViews[identifier]) {\n this._unusedReusableViews[identifier] = []\n }\n if (!this._unusedReusableViews[identifier].contains(row)) {\n this._unusedReusableViews[identifier].push(row)\n }\n }\n \n _scheduleDrawVisibleRows() {\n if (!this._isDrawVisibleRowsScheduled) {\n this._isDrawVisibleRowsScheduled = YES\n \n UIView.runFunctionBeforeNextFrame(() => {\n this._calculateAllPositions()\n this._drawVisibleRows()\n this.setNeedsLayout()\n this._isDrawVisibleRowsScheduled = NO\n })\n }\n }\n \n _drawVisibleRows() {\n \n if (!this.isMemberOfViewTree) {\n return\n }\n \n // Uses the unified method above\n const visibleIndexes = this.indexesForVisibleRows()\n \n // If no rows are visible, remove all current rows\n if (visibleIndexes.length === 0) {\n this._removeVisibleRows()\n return\n }\n \n const minIndex = visibleIndexes[0]\n const maxIndex = visibleIndexes[visibleIndexes.length - 1]\n \n const removedViews: UITableViewRowView[] = []\n const visibleRows: UITableViewRowView[] = []\n \n // 1. Identify rows that have moved off-screen\n this._visibleRows.forEach((row) => {\n if (IS_DEFINED(row._UITableViewRowIndex) &&\n (row._UITableViewRowIndex < minIndex || row._UITableViewRowIndex > maxIndex)) {\n \n // Persist state before removal\n this._persistedData[row._UITableViewRowIndex] = this.persistenceDataItemForRowWithIndex(\n row._UITableViewRowIndex,\n row\n )\n this._markReusableViewAsUnused(row)\n removedViews.push(row)\n }\n else {\n visibleRows.push(row)\n }\n })\n \n this._visibleRows = visibleRows\n \n // 2. Add new rows that have moved onto the screen\n visibleIndexes.forEach((rowIndex: number) => {\n // If the view is already in this._visibleRows, do nothing to it\n if (this.isRowWithIndexVisible(rowIndex)) {\n return\n }\n \n // Get view from reuse pool (marked as unused before) or make a new one\n const view: UITableViewRowView = this.viewForRowWithIndex(rowIndex)\n this._visibleRows.push(view)\n this.addSubview(view)\n \n // Ensure the row and all its children stay out of the natural tab order\n view.tabIndex = -1\n view.forEachViewInSubtree(subview => {\n subview.tabIndex = -1\n })\n })\n \n // 3. Clean up DOM\n removedViews.forEach(row => {\n // Check that the row has not been added back\n if (this._visibleRows.indexOf(row) == -1) {\n row.removeFromSuperview()\n }\n })\n \n // 4. Re-apply keyboard focus highlight after rows are re-rendered\n this._applyKeyboardFocusToVisibleRows()\n \n }\n \n \n visibleRowWithIndex(rowIndex: number | undefined): UIView | undefined {\n for (let i = 0; i < this._visibleRows.length; i++) {\n const row = this._visibleRows[i]\n if (row._UITableViewRowIndex == rowIndex) {\n return row\n }\n }\n }\n \n \n isRowWithIndexVisible(rowIndex: number) {\n return IS(this.visibleRowWithIndex(rowIndex))\n }\n \n \n reusableViewForIdentifier(identifier: string, rowIndex: number): UITableViewRowView {\n \n const visibleRowView = this.visibleRowWithIndex(rowIndex)\n if (visibleRowView?._UITableViewReusabilityIdentifier === identifier) {\n return visibleRowView\n }\n \n if (!this._unusedReusableViews[identifier]) {\n this._unusedReusableViews[identifier] = []\n }\n \n let view: UITableViewRowView\n \n if (this._unusedReusableViews[identifier]?.length) {\n \n view = this._unusedReusableViews[identifier].pop() as UITableViewRowView\n view._UITableViewRowIndex = rowIndex\n Object.assign(view, this._persistedData[rowIndex] || this.defaultRowPersistenceDataItem())\n \n }\n else {\n \n view = this.newReusableViewForIdentifier(identifier, this._rowIDIndex) as UITableViewRowView\n this._rowIDIndex = this._rowIDIndex + 1\n \n view.configureWithObject({\n _UITableViewReusabilityIdentifier: identifier,\n _UITableViewRowIndex: rowIndex,\n \n // Extend clearIntrinsicSizeCache so that when the row (or any of its\n // subviews) invalidates its own size cache, the table is notified to\n // re-measure that specific row index. EXTEND preserves the original\n // implementation and appends this behaviour after it.\n clearIntrinsicSizeCache: EXTEND(() => {\n const currentRowIndex = view._UITableViewRowIndex\n if (IS_DEFINED(currentRowIndex) && view.isMemberOfViewTree) {\n this.invalidateSizeOfRowWithIndex(currentRowIndex)\n this.setNeedsLayout()\n }\n })\n })\n \n Object.assign(view, this._persistedData[rowIndex] || this.defaultRowPersistenceDataItem())\n \n }\n \n // When all rows are uniform, opt the view into the shared height cache so\n // only the first measurement is ever computed for the whole table.\n if (this.allRowsHaveEqualHeight) {\n view.sharedIntrinsicSizeCacheIdentifier = this._equalRowHeightCacheIdentifier\n }\n else {\n view.sharedIntrinsicSizeCacheIdentifier = undefined\n }\n \n return view\n \n }\n \n \n // Functions that should be overridden to draw the correct content START\n newReusableViewForIdentifier(identifier: string, rowIDIndex: number): UIView {\n \n const view = new UIButton(this.elementID + \"Row\" + rowIDIndex)\n \n view.stopsPointerEventPropagation = NO\n view.pausesPointerEvents = NO\n \n return view\n \n }\n \n heightForRowWithIndex(index: number): number {\n return 50\n }\n \n numberOfRows() {\n return 10000\n }\n \n defaultRowPersistenceDataItem(): any {\n \n \n }\n \n persistenceDataItemForRowWithIndex(rowIndex: number, row: UIView): any {\n \n \n }\n \n viewForRowWithIndex(rowIndex: number): UITableViewRowView {\n const row = this.reusableViewForIdentifier(\"Row\", rowIndex)\n row._UITableViewRowIndex = rowIndex\n FIRST_OR_NIL((row as unknown as UIButton).titleLabel).text = \"Row \" + rowIndex\n return row\n }\n \n // Functions that should be overridden to draw the correct content END\n \n \n // Functions that trigger redrawing of the content\n override didScrollToPosition(offsetPosition: UIPoint) {\n \n super.didScrollToPosition(offsetPosition)\n \n this.forEachViewInSubtree((view: UIView) => {\n view._isPointerValid = NO\n })\n \n this._scheduleDrawVisibleRows()\n \n }\n \n override willMoveToSuperview(superview: UIView) {\n super.willMoveToSuperview(superview)\n \n if (IS(superview)) {\n // Set up viewport listeners when added to a superview\n this._setupViewportScrollAndResizeHandlersIfNeeded()\n }\n else {\n // Clean up when removed from superview\n this._cleanupViewportScrollListeners()\n }\n }\n \n override wasAddedToViewTree() {\n super.wasAddedToViewTree()\n this.loadData()\n \n // Ensure listeners are set up\n this._setupViewportScrollAndResizeHandlersIfNeeded()\n \n // Attach keyboard and pointer listeners now that the element is stable\n // in the DOM. Guarded so repeated wasAddedToViewTree calls (e.g. after\n // navigation returns) don't stack duplicate listeners.\n if (!this._keyboardListenersAttached) {\n this._keyboardListenersAttached = true\n const el = this._keyboardListenerElement\n \n el.addEventListener(\"keydown\", this._keydownHandler!)\n \n el.addEventListener(\"pointerdown\", (event: PointerEvent) => {\n const target = event.target as HTMLElement | null\n if (target?.tagName === \"INPUT\" || target?.tagName === \"TEXTAREA\") {\n return\n }\n let walkedTarget = target\n while (walkedTarget && walkedTarget !== el) {\n const viewObject = (walkedTarget as any).UIViewObject as UITableViewRowView | undefined\n if (viewObject?._UITableViewRowIndex !== undefined) {\n el.focus({ preventScroll: true })\n this._setKeyboardFocus(viewObject._UITableViewRowIndex, this._keyboardFocusedCellIndex)\n return\n }\n walkedTarget = walkedTarget.parentElement\n }\n el.focus({ preventScroll: true })\n })\n \n el.addEventListener(\"focus\", () => {\n if (this._keyboardFocusedRowIndex === undefined && this.numberOfRows() > 0) {\n this._setKeyboardFocus(0, this._keyboardFocusedCellIndex)\n }\n else if (this._keyboardFocusedRowIndex !== undefined) {\n this._applyKeyboardFocusToVisibleRows()\n }\n })\n \n el.addEventListener(\"blur\", (event: FocusEvent) => {\n if (!el.contains(event.relatedTarget as Node)) {\n this._applyKeyboardFocusToVisibleRows(true)\n }\n })\n }\n \n // Remove all subviews from the browser's natural tab order.\n // The container (_keyboardListenerElement) is the single tab stop.\n // Internal navigation is handled exclusively via arrow keys.\n this.forEachViewInSubtree(view => {\n if (view !== this) {\n view.tabIndex = -1\n }\n })\n // Re-assert tabIndex=0 on the listener element \u2014 wasAddedToViewTree fires\n // on every tree insertion including navigation returns.\n this._keyboardListenerElement.tabIndex = 0\n \n }\n \n override setFrame(rectangle: UIRectangle, zIndex?: number, performUncheckedLayout?: boolean) {\n \n const frame = this.frame\n super.setFrame(rectangle, zIndex, performUncheckedLayout)\n if (frame.isEqualTo(rectangle) && !performUncheckedLayout) {\n return\n }\n \n this._needsDrawingOfVisibleRowsBeforeLayout = YES\n \n }\n \n \n override didReceiveBroadcastEvent(event: UIViewBroadcastEvent) {\n \n super.didReceiveBroadcastEvent(event)\n \n if (event.name == UIView.broadcastEventName.LanguageChanged && this.reloadsOnLanguageChange) {\n \n this.reloadData()\n \n }\n \n \n }\n \n \n override clearIntrinsicSizeCache() {\n super.clearIntrinsicSizeCache()\n if (this.allRowsHaveEqualHeight) {\n UIView.invalidateSharedIntrinsicSizeCache(this._equalRowHeightCacheIdentifier)\n }\n this.invalidateSizeOfRowWithIndex(0)\n }\n \n private _layoutAllRows(positions = this._rowPositions) {\n \n const bounds = this.bounds\n \n const sortedRows = this._visibleRows.sort(\n (rowA, rowB) => rowA._UITableViewRowIndex! - rowB._UITableViewRowIndex!\n )\n \n sortedRows.forEach((row, i) => {\n \n const frame = bounds.copy()\n \n const positionObject = this._rowPositionWithIndex(row._UITableViewRowIndex!, positions)\n frame.min.y = positionObject.topY\n frame.max.y = positionObject.bottomY\n row.frame = frame\n \n row.style.width = \"\" + (bounds.width - this.sidePadding * 2).integerValue + \"px\"\n row.style.left = \"\" + this.sidePadding.integerValue + \"px\"\n \n // Set aria-rowindex (1-based per ARIA spec)\n row.viewHTMLElement.setAttribute(\"aria-rowindex\", String((row._UITableViewRowIndex ?? 0) + 1))\n \n // Insert before the correct next sibling so DOM order always matches\n // row index order. The nextSibling check makes this a no-op when the\n // element is already in the right position, avoiding unnecessary DOM\n // mutations and the focus loss that appendChild causes.\n const nextSiblingElement = sortedRows[i + 1]?.viewHTMLElement\n ?? this._fullHeightView.viewHTMLElement\n if (row.viewHTMLElement.nextSibling !== nextSiblingElement) {\n this.viewHTMLElement.insertBefore(row.viewHTMLElement, nextSiblingElement)\n }\n \n })\n \n // Use _rowPositionWithIndex rather than positions.lastElement.\n // When allRowsHaveEqualHeight = YES, _rowPositions contains only a single\n // entry (row 0). positions.lastElement.bottomY would therefore equal just\n // ONE row's height (e.g. 50px) instead of the full content height.\n // _rowPositionWithIndex correctly uses the computed formula for equal-height\n // tables (numberOfRows \u00D7 rowHeight) and falls back to positions[N-1] otherwise.\n // This ensures _fullHeightView maintains the correct scroll height even when\n // all visible rows have been removed from the DOM (e.g. after crossing the\n // bottom edge), preventing the browser from clamping scrollTop to 0.\n const numberOfRows = this.numberOfRows()\n const fullContentHeight = numberOfRows\n ? this._rowPositionWithIndex(numberOfRows - 1, positions).bottomY\n : 0\n this._fullHeightView.frame = bounds.rectangleWithHeight(fullContentHeight)\n .rectangleWithWidth(bounds.width * 0.5)\n \n }\n \n private _animateLayoutAllRows() {\n \n UIView.animateViewOrViewsWithDurationDelayAndFunction(\n this._visibleRows,\n this.animationDuration,\n 0,\n undefined,\n () => {\n \n this._layoutAllRows()\n \n },\n () => {\n \n \n }\n )\n \n }\n \n // UITableView has usesVirtualLayoutingForIntrinsicSizing = NO and is always\n // given a fixed viewport frame by its parent \u2014 so frame.height never changes\n // between layout passes. The base didLayoutSubviews compares frame.height and\n // therefore never propagates upward, even when row positions have changed and\n // intrinsicContentHeight now returns a different value.\n // We override here to track the intrinsic content height instead, so that\n // parents (e.g. CBDataView) get their cache invalidated and re-layout whenever\n // the total row stack height changes.\n override didLayoutSubviews() {\n this.viewController?.viewDidLayoutSubviews()\n \n if (!this.isVirtualLayouting && IS(this.superview) && this.isMemberOfViewTree) {\n const currentContentHeight = this.intrinsicContentHeight()\n if (currentContentHeight !== this._lastReportedHeight) {\n this._lastReportedHeight = currentContentHeight\n this.clearIntrinsicSizeCache()\n this.superview.setNeedsLayout()\n }\n }\n }\n \n \n override layoutSubviews() {\n \n if (this.isVirtualLayouting) {\n console.error(\"layout subviews called during virtual layouting on UITableView, \" +\n \"indicating a possible error in the layout system.\")\n return\n }\n \n const previousPositions: UITableViewReusableViewPositionObject[] = JSON.parse(\n JSON.stringify(this._rowPositions))\n \n const previousVisibleRowsLength = this._visibleRows.length\n \n if (this._needsDrawingOfVisibleRowsBeforeLayout) {\n \n this._drawVisibleRows()\n \n this._needsDrawingOfVisibleRowsBeforeLayout = NO\n \n }\n \n \n super.layoutSubviews()\n \n \n if (!this.numberOfRows() || !this.isMemberOfViewTree) {\n \n return\n \n }\n \n \n if (this._shouldAnimateNextLayout) {\n \n \n // Need to do layout with the previous positions\n \n this._layoutAllRows(previousPositions)\n \n \n if (previousVisibleRowsLength < this._visibleRows.length) {\n \n \n UIView.runFunctionBeforeNextFrame(() => {\n \n this._animateLayoutAllRows()\n \n })\n \n }\n else {\n \n this._animateLayoutAllRows()\n \n }\n \n \n this._shouldAnimateNextLayout = NO\n \n }\n else {\n \n this._calculateAllPositions()\n \n this._layoutAllRows()\n \n \n }\n \n \n }\n \n \n override intrinsicContentHeight(constrainingWidth = 0) {\n \n let result = 0\n this._calculateAllPositions()\n \n const numberOfRows = this.numberOfRows()\n if (numberOfRows) {\n result = this._rowPositionWithIndex(numberOfRows - 1).bottomY\n }\n \n return result\n \n }\n \n \n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { UIButton } from \"./UIButton\"\nimport { UINativeScrollView } from \"./UINativeScrollView\"\nimport { EXTEND, FIRST_OR_NIL, IF, IS, IS_DEFINED, MAKE_ID, nil, NO, YES } from \"./UIObject\"\nimport { UIPoint } from \"./UIPoint\"\nimport { UIRectangle } from \"./UIRectangle\"\nimport { UIView, UIViewBroadcastEvent } from \"./UIView\"\n\n\ninterface UITableViewRowView extends UIView {\n \n _UITableViewRowIndex?: number;\n \n}\n\n\nexport interface UITableViewReusableViewsContainerObject {\n \n [key: string]: UIView[];\n \n}\n\n\nexport interface UITableViewReusableViewPositionObject {\n \n bottomY: number;\n topY: number;\n \n isValid: boolean;\n \n}\n\n\nexport class UITableView extends UINativeScrollView {\n \n \n allRowsHaveEqualHeight: boolean = NO\n _visibleRows: UITableViewRowView[] = []\n \n /** Shared intrinsic size cache identifier used for all row views when\n * allRowsHaveEqualHeight is YES. Stable for the lifetime of the table;\n * the shared cache bucket is invalidated on reloadData and\n * clearIntrinsicSizeCache so the height is re-measured after data changes. */\n _equalRowHeightCacheIdentifier: string\n \n _rowPositions: UITableViewReusableViewPositionObject[] = []\n \n _highestValidRowPositionIndex: number = 0\n \n _unusedReusableViews: UITableViewReusableViewsContainerObject = {}\n \n _fullHeightView: UIView\n _rowIDIndex: number = 0\n reloadsOnLanguageChange = YES\n sidePadding = 0\n \n cellWeights?: number[]\n \n _persistedData: any[] = []\n _needsDrawingOfVisibleRowsBeforeLayout = NO\n _isDrawVisibleRowsScheduled = NO\n _shouldAnimateNextLayout?: boolean\n \n override usesVirtualLayoutingForIntrinsicSizing = NO\n \n override animationDuration = 0.25\n \n // Viewport scrolling properties\n _intersectionObserver?: IntersectionObserver\n \n // -------------------------------------------------------------------------\n // Keyboard navigation state\n // -------------------------------------------------------------------------\n \n /** Row index with -1 meaning the header row. undefined means no focus. */\n _keyboardFocusedRowIndex: number | undefined = undefined\n /** Cell index within the focused row. */\n _keyboardFocusedCellIndex: number = 0\n /** Total number of data columns (excludes left/right side cells). Set by CBDataView. */\n _columnCount: number = 0\n /** Called by UITableView when the focused row/cell changes. CBDataView overrides this. */\n keyboardFocusDidChange?: (rowIndex: number | undefined, cellIndex: number) => void\n /** Fired when Enter is pressed on a focused cell. Passes rowIndex and cellIndex. */\n keyboardDidActivateCell?: (rowIndex: number, cellIndex: number) => void\n \n _keydownHandler?: (event: KeyboardEvent) => void\n _keyboardListenersAttached = false\n \n \n get _reusableViews(): UITableViewReusableViewsContainerObject {\n \n const result: UITableViewReusableViewsContainerObject = {}\n \n const addView = (view: UIView) => {\n const identifier = view._UITableViewReusabilityIdentifier\n if (!identifier) {\n return\n }\n if (!result[identifier]) {\n result[identifier] = []\n }\n result[identifier].push(view)\n }\n \n this._visibleRows.forEach(addView)\n \n this._unusedReusableViews.forEach((views: UIView[]) => views.forEach(addView))\n \n return result\n \n }\n \n \n constructor(elementID?: string) {\n \n super(elementID)\n \n this._equalRowHeightCacheIdentifier = (elementID ?? MAKE_ID()) + \"_rowHeight\"\n \n this._fullHeightView = new UIView()\n this._fullHeightView.hidden = YES\n this._fullHeightView.userInteractionEnabled = NO\n this.addSubview(this._fullHeightView)\n \n this.scrollsX = NO\n \n this._setupViewportScrollAndResizeHandlersIfNeeded()\n this._setupGridAccessibility()\n this._setupKeyboardNavigation()\n \n }\n \n \n // -------------------------------------------------------------------------\n // ARIA / Accessibility setup\n // -------------------------------------------------------------------------\n \n /**\n * The element that receives tabIndex, ARIA grid role, and all keyboard/pointer\n * listeners. Defaults to the table's own element. CBDataView overrides this\n * to a container that wraps both the header and the table, so the focus ring\n * encompasses both.\n */\n _keyboardListenerElement: HTMLElement = this.viewHTMLElement\n \n _setupGridAccessibility() {\n const el = this._keyboardListenerElement\n el.setAttribute(\"role\", \"grid\")\n el.setAttribute(\"aria-rowcount\", \"0\")\n el.setAttribute(\"aria-colcount\", \"0\")\n el.tabIndex = 0\n }\n \n /** Called by CBDataView after descriptors change. */\n setColumnCount(count: number) {\n this._columnCount = count\n this._keyboardListenerElement.setAttribute(\"aria-colcount\", String(count))\n }\n \n /** Called by CBDataView after data loads. */\n setRowCount(count: number) {\n this._keyboardListenerElement.setAttribute(\"aria-rowcount\", String(count))\n }\n \n \n // -------------------------------------------------------------------------\n // Keyboard navigation\n // -------------------------------------------------------------------------\n \n _setupKeyboardNavigation() {\n \n this._keydownHandler = (event: KeyboardEvent) => {\n \n if (!this.isMemberOfViewTree) {\n return\n }\n \n const target = event.target as HTMLElement\n if (target.tagName === \"INPUT\" || target.tagName === \"TEXTAREA\") {\n return\n }\n \n const rowCount = this.numberOfRows()\n const hasHeader = this._keyboardFocusedRowIndex !== undefined\n \n if (event.key === \"ArrowDown\") {\n event.preventDefault()\n if (this._keyboardFocusedRowIndex === undefined) {\n this._setKeyboardFocus(0, this._keyboardFocusedCellIndex)\n }\n else if (event.metaKey || event.ctrlKey) {\n this._setKeyboardFocus(rowCount - 1, this._keyboardFocusedCellIndex)\n }\n else if (event.altKey) {\n const pageSize = Math.max(1, Math.floor(this.bounds.height / (this._heightForAnyRow() || 50)))\n const next = Math.min(\n (this._keyboardFocusedRowIndex < 0 ? 0 : this._keyboardFocusedRowIndex) + pageSize,\n rowCount - 1\n )\n this._setKeyboardFocus(next, this._keyboardFocusedCellIndex)\n }\n else if (this._keyboardFocusedRowIndex === -1) {\n this._setKeyboardFocus(0, this._keyboardFocusedCellIndex)\n }\n else if (this._keyboardFocusedRowIndex < rowCount - 1) {\n this._setKeyboardFocus(this._keyboardFocusedRowIndex + 1, this._keyboardFocusedCellIndex)\n }\n }\n else if (event.key === \"ArrowUp\") {\n event.preventDefault()\n if (this._keyboardFocusedRowIndex === undefined) {\n this._setKeyboardFocus(rowCount - 1, this._keyboardFocusedCellIndex)\n }\n else if (event.metaKey || event.ctrlKey) {\n this._setKeyboardFocus(-1, this._keyboardFocusedCellIndex)\n }\n else if (event.altKey) {\n const pageSize = Math.max(1, Math.floor(this.bounds.height / (this._heightForAnyRow() || 50)))\n const prev = Math.max(\n (this._keyboardFocusedRowIndex < 0 ? 0 : this._keyboardFocusedRowIndex) - pageSize, -1)\n this._setKeyboardFocus(prev, this._keyboardFocusedCellIndex)\n }\n else if (this._keyboardFocusedRowIndex === 0) {\n this._setKeyboardFocus(-1, this._keyboardFocusedCellIndex)\n }\n else if (this._keyboardFocusedRowIndex > 0) {\n this._setKeyboardFocus(this._keyboardFocusedRowIndex - 1, this._keyboardFocusedCellIndex)\n }\n }\n else if (event.key === \"ArrowRight\") {\n event.preventDefault()\n if (this._keyboardFocusedRowIndex !== undefined && this._columnCount > 0) {\n const nextCell = event.metaKey || event.ctrlKey\n ? this._columnCount - 1\n : Math.min(this._keyboardFocusedCellIndex + 1, this._columnCount - 1)\n this._setKeyboardFocus(this._keyboardFocusedRowIndex, nextCell)\n }\n }\n else if (event.key === \"ArrowLeft\") {\n event.preventDefault()\n if (this._keyboardFocusedRowIndex !== undefined && this._columnCount > 0) {\n const prevCell = event.metaKey || event.ctrlKey\n ? 0\n : Math.max(this._keyboardFocusedCellIndex - 1, 0)\n this._setKeyboardFocus(this._keyboardFocusedRowIndex, prevCell)\n }\n }\n else if (event.key === \"Home\") {\n event.preventDefault()\n if (this._keyboardFocusedRowIndex !== undefined) {\n this._setKeyboardFocus(this._keyboardFocusedRowIndex, 0)\n }\n }\n else if (event.key === \"End\") {\n event.preventDefault()\n if (this._keyboardFocusedRowIndex !== undefined && this._columnCount > 0) {\n this._setKeyboardFocus(this._keyboardFocusedRowIndex, this._columnCount - 1)\n }\n }\n else if (event.key === \"PageDown\") {\n event.preventDefault()\n if (this._keyboardFocusedRowIndex !== undefined) {\n const pageSize = Math.max(1, Math.floor(this.bounds.height / (this._heightForAnyRow() || 50)))\n const next = Math.min(\n (this._keyboardFocusedRowIndex < 0 ? 0 : this._keyboardFocusedRowIndex) + pageSize,\n rowCount - 1\n )\n this._setKeyboardFocus(next, this._keyboardFocusedCellIndex)\n }\n }\n else if (event.key === \"PageUp\") {\n event.preventDefault()\n if (this._keyboardFocusedRowIndex !== undefined) {\n const pageSize = Math.max(1, Math.floor(this.bounds.height / (this._heightForAnyRow() || 50)))\n const prev = Math.max(\n (this._keyboardFocusedRowIndex < 0 ? 0 : this._keyboardFocusedRowIndex) - pageSize, -1)\n this._setKeyboardFocus(prev, this._keyboardFocusedCellIndex)\n }\n }\n else if (event.key === \"Enter\" || event.key === \" \") {\n if (this._keyboardFocusedRowIndex !== undefined && this._keyboardFocusedRowIndex >= 0) {\n event.preventDefault()\n this.keyboardDidActivateCell?.(this._keyboardFocusedRowIndex, this._keyboardFocusedCellIndex)\n }\n }\n else if (event.key === \"Escape\") {\n // Release focus from the table \u2014 move to next focusable element\n this._clearKeyboardFocus()\n this._keyboardListenerElement.blur()\n }\n \n }\n \n // Listeners are attached in wasAddedToViewTree to guarantee they land\n // on the final stable viewHTMLElement after the framework has fully\n // initialised the view.\n \n }\n \n /**\n * Move keyboard focus to a specific row and cell.\n * rowIndex = -1 means the header row.\n */\n _setKeyboardFocus(rowIndex: number, cellIndex: number) {\n \n const previousRowIndex = this._keyboardFocusedRowIndex\n const previousCellIndex = this._keyboardFocusedCellIndex\n \n // When moving to a different data row, land on the first button cell by default\n if (rowIndex >= 0 && rowIndex !== previousRowIndex) {\n const row = this.visibleRowWithIndex(rowIndex) as any\n if (row && typeof row.firstButtonCellIndex === \"function\") {\n cellIndex = row.firstButtonCellIndex()\n }\n }\n \n this._keyboardFocusedRowIndex = rowIndex\n this._keyboardFocusedCellIndex = cellIndex\n \n // Clear highlight from old position\n if (previousRowIndex !== undefined && previousRowIndex !== rowIndex) {\n this._clearKeyboardFocusOnRow(previousRowIndex)\n }\n else if (previousRowIndex === rowIndex && previousCellIndex !== cellIndex) {\n this._clearKeyboardFocusOnRow(rowIndex)\n }\n \n // Scroll the focused row into view if it is a data row\n if (rowIndex >= 0) {\n this._scrollRowIntoView(rowIndex)\n }\n \n // Apply highlight to new position\n this._applyKeyboardFocusToVisibleRows()\n \n // Notify observers (CBDataView uses this to sync header highlight)\n this.keyboardFocusDidChange?.(rowIndex, cellIndex)\n \n }\n \n _clearKeyboardFocus() {\n const previous = this._keyboardFocusedRowIndex\n this._keyboardFocusedRowIndex = undefined\n if (previous !== undefined) {\n this._clearKeyboardFocusOnRow(previous)\n }\n this.keyboardFocusDidChange?.(undefined, this._keyboardFocusedCellIndex)\n }\n \n _clearKeyboardFocusOnRow(rowIndex: number) {\n if (rowIndex === -1) {\n // Header \u2014 notify via callback; CBDataView handles the header view\n this.keyboardFocusDidChange?.(-1, -1)\n return\n }\n const row = this.visibleRowWithIndex(rowIndex) as any\n if (row && typeof row.setKeyboardFocusedCellIndex === \"function\") {\n row.setKeyboardFocusedCellIndex(undefined)\n }\n }\n \n _applyKeyboardFocusToVisibleRows(clearAll = false) {\n this._visibleRows.forEach((row: any) => {\n if (typeof row.setKeyboardFocusedCellIndex !== \"function\") {\n return\n }\n if (clearAll || row._UITableViewRowIndex !== this._keyboardFocusedRowIndex) {\n row.setKeyboardFocusedCellIndex(undefined)\n }\n else {\n row.setKeyboardFocusedCellIndex(this._keyboardFocusedCellIndex)\n }\n })\n }\n \n _scrollRowIntoView(rowIndex: number) {\n const position = this._rowPositionWithIndex(rowIndex)\n if (!position) {\n return\n }\n const offsetY = this.contentOffset.y\n const visibleHeight = this.bounds.height\n if (position.topY < offsetY) {\n const duration = this.animationDuration\n this.animationDuration = 0\n this.contentOffset = this.contentOffset.pointWithY(position.topY)\n this.animationDuration = duration\n }\n else if (position.bottomY > offsetY + visibleHeight) {\n const duration = this.animationDuration\n this.animationDuration = 0\n this.contentOffset = this.contentOffset.pointWithY(position.bottomY - visibleHeight)\n this.animationDuration = duration\n }\n }\n \n /** Expose so CBDataView can call it after loading data. */\n focusRowAtIndex(rowIndex: number, cellIndex: number = 0) {\n this._setKeyboardFocus(rowIndex, cellIndex)\n this._keyboardListenerElement.focus({ preventScroll: true })\n }\n \n \n _windowScrollHandler = () => {\n if (!this.isMemberOfViewTree) {\n return\n }\n this._scheduleDrawVisibleRows()\n }\n \n _resizeHandler = () => {\n if (!this.isMemberOfViewTree) {\n return\n }\n // Invalidate all row positions on resize as widths may have changed\n this._rowPositions.everyElement.isValid = NO\n this._highestValidRowPositionIndex = -1\n this._scheduleDrawVisibleRows()\n }\n \n _setupViewportScrollAndResizeHandlersIfNeeded() {\n if (this._intersectionObserver) {\n return\n }\n \n window.addEventListener(\"scroll\", this._windowScrollHandler, { passive: true })\n window.addEventListener(\"resize\", this._resizeHandler, { passive: true })\n \n // Use IntersectionObserver to detect when table enters/exits viewport\n this._intersectionObserver = new IntersectionObserver(\n (entries) => {\n entries.forEach(entry => {\n if (entry.isIntersecting && this.isMemberOfViewTree) {\n this._scheduleDrawVisibleRows()\n }\n })\n },\n {\n root: null,\n rootMargin: \"100% 0px\", // Load rows 100% viewport height before/after\n threshold: 0\n }\n )\n this._intersectionObserver.observe(this.viewHTMLElement)\n }\n \n \n _cleanupViewportScrollListeners() {\n window.removeEventListener(\"scroll\", this._windowScrollHandler)\n window.removeEventListener(\"resize\", this._resizeHandler)\n if (this._intersectionObserver) {\n this._intersectionObserver.disconnect()\n this._intersectionObserver = undefined\n }\n }\n \n override wasRemovedFromViewTree() {\n super.wasRemovedFromViewTree()\n this._cleanupViewportScrollListeners()\n if (this._keydownHandler) {\n this.viewHTMLElement.removeEventListener(\"keydown\", this._keydownHandler)\n }\n // Reset so listeners are re-attached if added back to the tree\n this._keyboardListenersAttached = false\n }\n \n \n loadData() {\n \n this._persistedData = []\n \n this._calculatePositionsUntilIndex(this.numberOfRows() - 1)\n this._needsDrawingOfVisibleRowsBeforeLayout = YES\n \n this.setNeedsLayout()\n \n }\n \n reloadData() {\n \n this._removeVisibleRows()\n this._removeAllReusableRows()\n \n this._rowPositions = []\n this._highestValidRowPositionIndex = -1\n \n if (this.allRowsHaveEqualHeight) {\n UIView.invalidateSharedIntrinsicSizeCache(this._equalRowHeightCacheIdentifier)\n }\n \n this.loadData()\n \n }\n \n \n highlightChanges(previousData: any[], newData: any[]) {\n \n previousData = previousData.map(dataPoint => JSON.stringify(dataPoint))\n newData = newData.map(dataPoint => JSON.stringify(dataPoint))\n \n const newIndexes: number[] = []\n \n newData.forEach((value, index) => {\n \n if (!previousData.contains(value)) {\n \n newIndexes.push(index)\n \n }\n \n })\n \n newIndexes.forEach(index => {\n \n if (this.isRowWithIndexVisible(index)) {\n this.highlightRowAsNew(\n this.visibleRowWithIndex(index) ?? this.viewForRowWithIndex(index)\n )\n }\n \n })\n \n }\n \n \n highlightRowAsNew(row: UIView) {\n \n \n }\n \n \n invalidateSizeOfRowWithIndex(index: number, animateChange = NO) {\n if (this._rowPositions?.[index]) {\n FIRST_OR_NIL(this._rowPositions[index]).isValid = NO\n this._rowPositions.slice(index).everyElement.isValid = NO\n }\n this._highestValidRowPositionIndex = Math.min(this._highestValidRowPositionIndex, index - 1)\n this._needsDrawingOfVisibleRowsBeforeLayout = YES\n this._shouldAnimateNextLayout = animateChange\n }\n \n \n _rowPositionWithIndex(index: number, positions = this._rowPositions) {\n if (this.allRowsHaveEqualHeight && index > 0) {\n const firstPositionObject = positions[0]\n const rowHeight = firstPositionObject.bottomY - firstPositionObject.topY\n const result = {\n bottomY: rowHeight * (index + 1),\n topY: rowHeight * index,\n isValid: firstPositionObject.isValid\n }\n return result\n }\n return positions[index]\n }\n \n _calculateAllPositions() {\n this._calculatePositionsUntilIndex(this.numberOfRows() - 1)\n }\n \n _calculatePositionsUntilIndex(maxIndex: number) {\n \n if (this.allRowsHaveEqualHeight) {\n const positionObject: UITableViewReusableViewPositionObject = {\n bottomY: this._heightForAnyRow(),\n topY: 0,\n isValid: YES\n }\n this._rowPositions = [positionObject]\n return\n }\n \n let validPositionObject = this._rowPositions[this._highestValidRowPositionIndex]\n if (!IS(validPositionObject)) {\n validPositionObject = {\n bottomY: 0,\n topY: 0,\n isValid: YES\n }\n }\n \n let previousBottomY = validPositionObject.bottomY\n if (!this._rowPositions.length) {\n this._highestValidRowPositionIndex = -1\n }\n \n for (let i = this._highestValidRowPositionIndex + 1; i <= maxIndex; i++) {\n \n let height: number\n \n const rowPositionObject = this._rowPositions[i]\n \n if (IS((rowPositionObject || nil).isValid)) {\n height = rowPositionObject.bottomY - rowPositionObject.topY\n }\n // Do not calculate heights if all rows have equal heights, and we already have a height\n else if (this.allRowsHaveEqualHeight && i > 0) {\n height = this._rowPositions[0].bottomY - this._rowPositions[0].topY\n }\n else {\n height = this.heightForRowWithIndex(i)\n }\n \n const positionObject: UITableViewReusableViewPositionObject = {\n bottomY: previousBottomY + height,\n topY: previousBottomY,\n isValid: YES\n }\n \n if (i < this._rowPositions.length) {\n this._rowPositions[i] = positionObject\n }\n else {\n this._rowPositions.push(positionObject)\n }\n this._highestValidRowPositionIndex = i\n previousBottomY = previousBottomY + height\n \n }\n \n }\n \n \n _heightForAnyRow(calculateVisibleRows = YES) {\n return this.heightForRowWithIndex(\n this._visibleRows.firstElement?._UITableViewRowIndex ??\n (calculateVisibleRows ? this.indexesForVisibleRows().firstElement : 0) ??\n 0\n )\n }\n \n indexesForVisibleRows(paddingRatio = 0.5): number[] {\n \n // 1. Calculate the visible frame relative to the Table's bounds (0,0 is top-left of the table view)\n // This accounts for the Window viewport clipping the table if it is partially off-screen.\n const tableRect = this.viewHTMLElement.getBoundingClientRect()\n const viewportHeight = window.innerHeight\n const pageScale = UIView.pageScale\n \n // The top of the visible window relative to the view's top edge.\n // If tableRect.top is negative, the table is scrolled up and clipped by the window top.\n const visibleFrameTop = Math.max(0, -tableRect.top / pageScale)\n \n // The bottom of the visible window relative to the view's top edge.\n // We clip it to the table's actual bounds height so we don't look past the table content.\n const visibleFrameBottom = Math.min(\n this.bounds.height,\n (viewportHeight - tableRect.top) / pageScale\n )\n \n // If the table is completely off-screen, return empty\n if (visibleFrameBottom <= visibleFrameTop) {\n return []\n }\n \n // 2. Convert to Content Coordinates (Scroll Offset)\n // contentOffset.y is the internal scroll position.\n // If using viewport scrolling (full height), contentOffset.y is typically 0.\n // If using internal scrolling, this shifts the visible frame to the correct content rows.\n let firstVisibleY = this.contentOffset.y + visibleFrameTop\n let lastVisibleY = this.contentOffset.y + visibleFrameBottom\n \n // 3. Apply Padding\n // We calculate padding based on the viewport height to ensure smooth scrolling\n const paddingPx = (viewportHeight / pageScale) * paddingRatio\n firstVisibleY = Math.max(0, firstVisibleY - paddingPx)\n lastVisibleY = lastVisibleY + paddingPx\n \n const numberOfRows = this.numberOfRows()\n \n // 4. Find Indexes\n if (this.allRowsHaveEqualHeight) {\n \n const rowHeight = this._heightForAnyRow(NO)\n \n let firstIndex = Math.floor(firstVisibleY / rowHeight)\n let lastIndex = Math.floor(lastVisibleY / rowHeight)\n \n // Clamp BOTH indexes to [0, numberOfRows-1].\n // Without the upper clamp on firstIndex, when the viewport extends below the\n // last row firstIndex can exceed numberOfRows-1 while lastIndex is already\n // clamped there. firstIndex > lastIndex \u2192 empty result \u2192 _removeVisibleRows \u2192\n // browser collapses scrollHeight \u2192 scrollTop resets to 0 \u2192 rows pile at top.\n firstIndex = Math.max(0, Math.min(firstIndex, numberOfRows - 1))\n lastIndex = Math.max(0, Math.min(lastIndex, numberOfRows - 1))\n \n const result = []\n for (let i = firstIndex; i <= lastIndex; i++) {\n result.push(i)\n }\n return result\n }\n \n // Variable Heights\n this._calculateAllPositions()\n const result = []\n \n // Clamp firstVisibleY to the actual content height so that when the viewport\n // extends below the last row the intersection check still matches the final rows\n // rather than producing an empty result.\n const totalContentHeight = IF(this._rowPositions.lastElement)(() =>\n this._rowPositions.lastElement.bottomY\n ).ELSE(() =>\n 0\n )\n firstVisibleY = Math.min(firstVisibleY, totalContentHeight)\n \n for (let i = 0; i < numberOfRows; i++) {\n \n const position = this._rowPositionWithIndex(i)\n if (!position) {\n break\n }\n \n const rowTop = position.topY\n const rowBottom = position.bottomY\n \n // Check intersection\n if (rowBottom >= firstVisibleY && rowTop <= lastVisibleY) {\n result.push(i)\n }\n \n if (rowTop > lastVisibleY) {\n break\n }\n \n }\n \n return result\n \n }\n \n \n // This is called when no rows are supposed to be visible as a performance shortcut\n _removeVisibleRows() {\n \n this._visibleRows.forEach((row: UIView) => {\n \n this._persistedData[row._UITableViewRowIndex as number] = this.persistenceDataItemForRowWithIndex(\n row._UITableViewRowIndex as number,\n row\n )\n row.removeFromSuperview()\n this._markReusableViewAsUnused(row)\n \n })\n this._visibleRows = []\n \n }\n \n \n _removeAllReusableRows() {\n this._unusedReusableViews.forEach((rows: UIView[]) =>\n rows.forEach((row: UIView) => {\n \n this._persistedData[row._UITableViewRowIndex as number] = this.persistenceDataItemForRowWithIndex(\n row._UITableViewRowIndex as number,\n row\n )\n row.removeFromSuperview()\n \n })\n )\n this._unusedReusableViews = {}\n }\n \n \n _markReusableViewAsUnused(row: UIView) {\n const identifier = row._UITableViewReusabilityIdentifier\n if (!this._unusedReusableViews[identifier]) {\n this._unusedReusableViews[identifier] = []\n }\n if (!this._unusedReusableViews[identifier].contains(row)) {\n this._unusedReusableViews[identifier].push(row)\n }\n }\n \n _scheduleDrawVisibleRows() {\n if (!this._isDrawVisibleRowsScheduled) {\n this._isDrawVisibleRowsScheduled = YES\n \n UIView.runFunctionBeforeNextFrame(() => {\n this._calculateAllPositions()\n this._drawVisibleRows()\n this.setNeedsLayout()\n this._isDrawVisibleRowsScheduled = NO\n })\n }\n }\n \n _drawVisibleRows() {\n \n if (!this.isMemberOfViewTree) {\n return\n }\n \n // Uses the unified method above\n const visibleIndexes = this.indexesForVisibleRows()\n \n // If no rows are visible, remove all current rows\n if (visibleIndexes.length === 0) {\n this._removeVisibleRows()\n return\n }\n \n const minIndex = visibleIndexes[0]\n const maxIndex = visibleIndexes[visibleIndexes.length - 1]\n \n const removedViews: UITableViewRowView[] = []\n const visibleRows: UITableViewRowView[] = []\n \n // 1. Identify rows that have moved off-screen\n this._visibleRows.forEach((row) => {\n if (IS_DEFINED(row._UITableViewRowIndex) &&\n (row._UITableViewRowIndex < minIndex || row._UITableViewRowIndex > maxIndex)) {\n \n // Persist state before removal\n this._persistedData[row._UITableViewRowIndex] = this.persistenceDataItemForRowWithIndex(\n row._UITableViewRowIndex,\n row\n )\n this._markReusableViewAsUnused(row)\n removedViews.push(row)\n }\n else {\n visibleRows.push(row)\n }\n })\n \n this._visibleRows = visibleRows\n \n // 2. Add new rows that have moved onto the screen\n visibleIndexes.forEach((rowIndex: number) => {\n // If the view is already in this._visibleRows, do nothing to it\n if (this.isRowWithIndexVisible(rowIndex)) {\n return\n }\n \n // Get view from reuse pool (marked as unused before) or make a new one\n const view: UITableViewRowView = this.viewForRowWithIndex(rowIndex)\n this._visibleRows.push(view)\n this.addSubview(view)\n \n // Ensure the row and all its children stay out of the natural tab order\n view.tabIndex = -1\n view.forEachViewInSubtree(subview => {\n subview.tabIndex = -1\n })\n })\n \n // 3. Clean up DOM\n removedViews.forEach(row => {\n // Check that the row has not been added back\n if (this._visibleRows.indexOf(row) == -1) {\n row.removeFromSuperview()\n }\n })\n \n // 4. Re-apply keyboard focus highlight after rows are re-rendered\n this._applyKeyboardFocusToVisibleRows()\n \n }\n \n \n visibleRowWithIndex(rowIndex: number | undefined): UIView | undefined {\n for (let i = 0; i < this._visibleRows.length; i++) {\n const row = this._visibleRows[i]\n if (row._UITableViewRowIndex == rowIndex) {\n return row\n }\n }\n }\n \n \n isRowWithIndexVisible(rowIndex: number) {\n return IS(this.visibleRowWithIndex(rowIndex))\n }\n \n \n reusableViewForIdentifier(identifier: string, rowIndex: number): UITableViewRowView {\n \n const visibleRowView = this.visibleRowWithIndex(rowIndex)\n if (visibleRowView?._UITableViewReusabilityIdentifier === identifier) {\n return visibleRowView\n }\n \n if (!this._unusedReusableViews[identifier]) {\n this._unusedReusableViews[identifier] = []\n }\n \n let view: UITableViewRowView\n \n if (this._unusedReusableViews[identifier]?.length) {\n \n view = this._unusedReusableViews[identifier].pop() as UITableViewRowView\n view._UITableViewRowIndex = rowIndex\n Object.assign(view, this._persistedData[rowIndex] || this.defaultRowPersistenceDataItem())\n \n }\n else {\n \n view = this.newReusableViewForIdentifier(identifier, this._rowIDIndex) as UITableViewRowView\n this._rowIDIndex = this._rowIDIndex + 1\n \n view.configureWithObject({\n _UITableViewReusabilityIdentifier: identifier,\n _UITableViewRowIndex: rowIndex,\n \n // Extend clearIntrinsicSizeCache so that when the row (or any of its\n // subviews) invalidates its own size cache, the table is notified to\n // re-measure that specific row index. EXTEND preserves the original\n // implementation and appends this behaviour after it.\n clearIntrinsicSizeCache: EXTEND(() => {\n const currentRowIndex = view._UITableViewRowIndex\n if (IS_DEFINED(currentRowIndex) && view.isMemberOfViewTree) {\n this.invalidateSizeOfRowWithIndex(currentRowIndex)\n this.setNeedsLayout()\n }\n })\n })\n \n Object.assign(view, this._persistedData[rowIndex] || this.defaultRowPersistenceDataItem())\n \n }\n \n // When all rows are uniform, opt the view into the shared height cache so\n // only the first measurement is ever computed for the whole table.\n if (this.allRowsHaveEqualHeight) {\n view.sharedIntrinsicSizeCacheIdentifier = this._equalRowHeightCacheIdentifier\n }\n else {\n view.sharedIntrinsicSizeCacheIdentifier = undefined\n }\n \n return view\n \n }\n \n \n // Functions that should be overridden to draw the correct content START\n newReusableViewForIdentifier(identifier: string, rowIDIndex: number): UIView {\n \n const view = new UIButton(this.elementID + \"Row\" + rowIDIndex)\n \n view.stopsPointerEventPropagation = NO\n view.pausesPointerEvents = NO\n \n return view\n \n }\n \n heightForRowWithIndex(index: number): number {\n return 50\n }\n \n numberOfRows() {\n return 10000\n }\n \n defaultRowPersistenceDataItem(): any {\n \n \n }\n \n persistenceDataItemForRowWithIndex(rowIndex: number, row: UIView): any {\n \n \n }\n \n viewForRowWithIndex(rowIndex: number): UITableViewRowView {\n const row = this.reusableViewForIdentifier(\"Row\", rowIndex)\n row._UITableViewRowIndex = rowIndex\n FIRST_OR_NIL((row as unknown as UIButton).titleLabel).text = \"Row \" + rowIndex\n return row\n }\n \n // Functions that should be overridden to draw the correct content END\n \n \n // Functions that trigger redrawing of the content\n override didScrollToPosition(offsetPosition: UIPoint) {\n \n super.didScrollToPosition(offsetPosition)\n \n this.forEachViewInSubtree((view: UIView) => {\n view._isPointerValid = NO\n })\n \n this._scheduleDrawVisibleRows()\n \n }\n \n override willMoveToSuperview(superview: UIView) {\n super.willMoveToSuperview(superview)\n \n if (IS(superview)) {\n // Set up viewport listeners when added to a superview\n this._setupViewportScrollAndResizeHandlersIfNeeded()\n }\n else {\n // Clean up when removed from superview\n this._cleanupViewportScrollListeners()\n }\n }\n \n override wasAddedToViewTree() {\n super.wasAddedToViewTree()\n this.loadData()\n \n // Ensure listeners are set up\n this._setupViewportScrollAndResizeHandlersIfNeeded()\n \n // Attach keyboard and pointer listeners now that the element is stable\n // in the DOM. Guarded so repeated wasAddedToViewTree calls (e.g. after\n // navigation returns) don't stack duplicate listeners.\n if (!this._keyboardListenersAttached) {\n this._keyboardListenersAttached = true\n const el = this._keyboardListenerElement\n \n el.addEventListener(\"keydown\", this._keydownHandler!)\n \n el.addEventListener(\"pointerdown\", (event: PointerEvent) => {\n const target = event.target as HTMLElement | null\n if (target?.tagName === \"INPUT\" || target?.tagName === \"TEXTAREA\") {\n return\n }\n let walkedTarget = target\n while (walkedTarget && walkedTarget !== el) {\n const viewObject = (walkedTarget as any).UIViewObject as UITableViewRowView | undefined\n if (viewObject?._UITableViewRowIndex !== undefined) {\n this._setKeyboardFocus(viewObject._UITableViewRowIndex, this._keyboardFocusedCellIndex)\n el.focus({ preventScroll: true })\n return\n }\n walkedTarget = walkedTarget.parentElement\n }\n el.focus({ preventScroll: true })\n })\n \n el.addEventListener(\"focus\", () => {\n if (this._keyboardFocusedRowIndex === undefined && this.numberOfRows() > 0) {\n this._setKeyboardFocus(0, this._keyboardFocusedCellIndex)\n }\n else if (this._keyboardFocusedRowIndex !== undefined) {\n this._applyKeyboardFocusToVisibleRows()\n }\n })\n \n el.addEventListener(\"blur\", (event: FocusEvent) => {\n if (!el.contains(event.relatedTarget as Node)) {\n this._applyKeyboardFocusToVisibleRows(true)\n }\n })\n }\n \n // Remove all subviews from the browser's natural tab order.\n // The container (_keyboardListenerElement) is the single tab stop.\n // Internal navigation is handled exclusively via arrow keys.\n this.forEachViewInSubtree(view => {\n if (view !== this) {\n view.tabIndex = -1\n }\n })\n // Re-assert tabIndex=0 on the listener element \u2014 wasAddedToViewTree fires\n // on every tree insertion including navigation returns.\n this._keyboardListenerElement.tabIndex = 0\n \n }\n \n override setFrame(rectangle: UIRectangle, zIndex?: number, performUncheckedLayout?: boolean) {\n \n const frame = this.frame\n super.setFrame(rectangle, zIndex, performUncheckedLayout)\n if (frame.isEqualTo(rectangle) && !performUncheckedLayout) {\n return\n }\n \n this._needsDrawingOfVisibleRowsBeforeLayout = YES\n \n }\n \n \n override didReceiveBroadcastEvent(event: UIViewBroadcastEvent) {\n \n super.didReceiveBroadcastEvent(event)\n \n if (event.name == UIView.broadcastEventName.LanguageChanged && this.reloadsOnLanguageChange) {\n \n this.reloadData()\n \n }\n \n \n }\n \n \n override clearIntrinsicSizeCache() {\n super.clearIntrinsicSizeCache()\n if (this.allRowsHaveEqualHeight) {\n UIView.invalidateSharedIntrinsicSizeCache(this._equalRowHeightCacheIdentifier)\n }\n this.invalidateSizeOfRowWithIndex(0)\n }\n \n private _layoutAllRows(positions = this._rowPositions) {\n \n const bounds = this.bounds\n \n const sortedRows = this._visibleRows.sort(\n (rowA, rowB) => rowA._UITableViewRowIndex! - rowB._UITableViewRowIndex!\n )\n \n sortedRows.forEach((row, i) => {\n \n const frame = bounds.copy()\n \n const positionObject = this._rowPositionWithIndex(row._UITableViewRowIndex!, positions)\n frame.min.y = positionObject.topY\n frame.max.y = positionObject.bottomY\n row.frame = frame\n \n row.style.width = \"\" + (bounds.width - this.sidePadding * 2).integerValue + \"px\"\n row.style.left = \"\" + this.sidePadding.integerValue + \"px\"\n \n // Set aria-rowindex (1-based per ARIA spec)\n row.viewHTMLElement.setAttribute(\"aria-rowindex\", String((row._UITableViewRowIndex ?? 0) + 1))\n \n // Insert before the correct next sibling so DOM order always matches\n // row index order. The nextSibling check makes this a no-op when the\n // element is already in the right position, avoiding unnecessary DOM\n // mutations and the focus loss that appendChild causes.\n const nextSiblingElement = sortedRows[i + 1]?.viewHTMLElement\n ?? this._fullHeightView.viewHTMLElement\n if (row.viewHTMLElement.nextSibling !== nextSiblingElement) {\n this.viewHTMLElement.insertBefore(row.viewHTMLElement, nextSiblingElement)\n }\n \n })\n \n // Use _rowPositionWithIndex rather than positions.lastElement.\n // When allRowsHaveEqualHeight = YES, _rowPositions contains only a single\n // entry (row 0). positions.lastElement.bottomY would therefore equal just\n // ONE row's height (e.g. 50px) instead of the full content height.\n // _rowPositionWithIndex correctly uses the computed formula for equal-height\n // tables (numberOfRows \u00D7 rowHeight) and falls back to positions[N-1] otherwise.\n // This ensures _fullHeightView maintains the correct scroll height even when\n // all visible rows have been removed from the DOM (e.g. after crossing the\n // bottom edge), preventing the browser from clamping scrollTop to 0.\n const numberOfRows = this.numberOfRows()\n const fullContentHeight = numberOfRows\n ? this._rowPositionWithIndex(numberOfRows - 1, positions).bottomY\n : 0\n this._fullHeightView.frame = bounds.rectangleWithHeight(fullContentHeight)\n .rectangleWithWidth(bounds.width * 0.5)\n \n }\n \n private _animateLayoutAllRows() {\n \n UIView.animateViewOrViewsWithDurationDelayAndFunction(\n this._visibleRows,\n this.animationDuration,\n 0,\n undefined,\n () => {\n \n this._layoutAllRows()\n \n },\n () => {\n \n \n }\n )\n \n }\n \n // UITableView has usesVirtualLayoutingForIntrinsicSizing = NO and is always\n // given a fixed viewport frame by its parent \u2014 so frame.height never changes\n // between layout passes. The base didLayoutSubviews compares frame.height and\n // therefore never propagates upward, even when row positions have changed and\n // intrinsicContentHeight now returns a different value.\n // We override here to track the intrinsic content height instead, so that\n // parents (e.g. CBDataView) get their cache invalidated and re-layout whenever\n // the total row stack height changes.\n override didLayoutSubviews() {\n this.viewController?.viewDidLayoutSubviews()\n \n if (!this.isVirtualLayouting && IS(this.superview) && this.isMemberOfViewTree) {\n const currentContentHeight = this.intrinsicContentHeight()\n if (currentContentHeight !== this._lastReportedHeight) {\n this._lastReportedHeight = currentContentHeight\n this.clearIntrinsicSizeCache()\n this.superview.setNeedsLayout()\n }\n }\n }\n \n \n override layoutSubviews() {\n \n if (this.isVirtualLayouting) {\n console.error(\"layout subviews called during virtual layouting on UITableView, \" +\n \"indicating a possible error in the layout system.\")\n return\n }\n \n const previousPositions: UITableViewReusableViewPositionObject[] = JSON.parse(\n JSON.stringify(this._rowPositions))\n \n const previousVisibleRowsLength = this._visibleRows.length\n \n if (this._needsDrawingOfVisibleRowsBeforeLayout) {\n \n this._drawVisibleRows()\n \n this._needsDrawingOfVisibleRowsBeforeLayout = NO\n \n }\n \n \n super.layoutSubviews()\n \n \n if (!this.numberOfRows() || !this.isMemberOfViewTree) {\n \n return\n \n }\n \n \n if (this._shouldAnimateNextLayout) {\n \n \n // Need to do layout with the previous positions\n \n this._layoutAllRows(previousPositions)\n \n \n if (previousVisibleRowsLength < this._visibleRows.length) {\n \n \n UIView.runFunctionBeforeNextFrame(() => {\n \n this._animateLayoutAllRows()\n \n })\n \n }\n else {\n \n this._animateLayoutAllRows()\n \n }\n \n \n this._shouldAnimateNextLayout = NO\n \n }\n else {\n \n this._calculateAllPositions()\n \n this._layoutAllRows()\n \n \n }\n \n \n }\n \n \n override intrinsicContentHeight(constrainingWidth = 0) {\n \n let result = 0\n this._calculateAllPositions()\n \n const numberOfRows = this.numberOfRows()\n if (numberOfRows) {\n result = this._rowPositionWithIndex(numberOfRows - 1).bottomY\n }\n \n return result\n \n }\n \n \n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["result"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uicore-ts",
3
- "version": "1.1.355",
3
+ "version": "1.1.357",
4
4
  "description": "UICore is a library to build native-like user interfaces using pure Typescript. No HTML is needed at all. Components are described as TS classes and all user interactions are handled explicitly. This library is strongly inspired by the UIKit framework that is used in IOS. In addition, UICore has tools to handle URL based routing, array sorting and filtering and adds a number of other utilities for convenience.",
5
5
  "main": "compiledScripts/index.js",
6
6
  "types": "compiledScripts/index.d.ts",
@@ -66,8 +66,18 @@ export class UIAutocompleteTextField<T = string> extends UITextField {
66
66
  }
67
67
  }
68
68
 
69
- // Close on blur
70
- this.controlEventTargetAccumulator.Blur = () => {
69
+ // Close on blur — but not when focus is moving into the dropdown itself
70
+ // (e.g. the user clicked a row). relatedTarget is the element receiving
71
+ // focus, so if it is inside the dropdown's element tree, keep it open and
72
+ // let the row's PointerUpInside / didSelectItem complete the selection.
73
+ this.controlEventTargetAccumulator.Blur = (sender, event) => {
74
+ const focusEvent = event as FocusEvent
75
+ if (
76
+ IS(focusEvent.relatedTarget) &&
77
+ this._dropdownView.viewHTMLElement.contains(focusEvent.relatedTarget as Node)
78
+ ) {
79
+ return
80
+ }
71
81
  this.closeDropdown()
72
82
  }
73
83
 
@@ -368,7 +378,13 @@ export class UIAutocompleteTextField<T = string> extends UITextField {
368
378
 
369
379
  this._isDropdownOpen = YES
370
380
  this._dropdownView.filterWords = []
371
- this.updateFilteredItems()
381
+ this._dropdownView.filteredItems = this._autocompleteItems
382
+ if (this._dropdownView.filteredItems.length > 0) {
383
+ const matchIndex = this._autocompleteItems.findIndex(
384
+ item => item.label === this.text
385
+ )
386
+ this._dropdownView.highlightedRowIndex = matchIndex !== -1 ? matchIndex : 0
387
+ }
372
388
  this._dropdownView.showAnchoredToView(this)
373
389
 
374
390
  }
@@ -1027,8 +1027,8 @@ export class UITableView extends UINativeScrollView {
1027
1027
  while (walkedTarget && walkedTarget !== el) {
1028
1028
  const viewObject = (walkedTarget as any).UIViewObject as UITableViewRowView | undefined
1029
1029
  if (viewObject?._UITableViewRowIndex !== undefined) {
1030
- el.focus({ preventScroll: true })
1031
1030
  this._setKeyboardFocus(viewObject._UITableViewRowIndex, this._keyboardFocusedCellIndex)
1031
+ el.focus({ preventScroll: true })
1032
1032
  return
1033
1033
  }
1034
1034
  walkedTarget = walkedTarget.parentElement