@octane-xplat/table 0.0.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +427 -3
  3. package/dist/native/ColumnDrag.tsrx.js +237 -0
  4. package/dist/native/DataGrid.tsrx.js +957 -0
  5. package/dist/native/GridControls.tsrx.js +424 -0
  6. package/dist/native/grid-accessibility.js +12 -0
  7. package/dist/native/grid-layout.js +43 -0
  8. package/dist/native/index.js +4 -0
  9. package/dist/native/octane-client-build.json +8 -0
  10. package/dist/native/table.js +56 -0
  11. package/dist/native/useTable.tsrx.js +24 -0
  12. package/dist/web/ColumnDrag.tsrx.js +85 -0
  13. package/dist/web/DataGrid.tsrx.js +242 -0
  14. package/dist/web/GridControls.tsrx.js +114 -0
  15. package/dist/web/grid-accessibility.js +12 -0
  16. package/dist/web/grid-layout.js +43 -0
  17. package/dist/web/index.js +4 -0
  18. package/dist/web/octane-client-build.json +8 -0
  19. package/dist/web/table.js +56 -0
  20. package/dist/web/useTable.tsrx.js +26 -0
  21. package/package.json +75 -3
  22. package/src/ColumnDrag.tsrx +123 -0
  23. package/src/DataGrid.mobile.test.tsrx +55 -0
  24. package/src/DataGrid.tsrx +350 -0
  25. package/src/DataGrid.web.test.tsrx +277 -0
  26. package/src/GridControls.tsrx +157 -0
  27. package/src/grid-accessibility.ts +14 -0
  28. package/src/grid-layout.test.ts +32 -0
  29. package/src/grid-layout.ts +52 -0
  30. package/src/index.macos.ts +8 -0
  31. package/src/index.ts +7 -0
  32. package/src/index.web.ts +8 -0
  33. package/src/props.ts +122 -0
  34. package/src/table.test.ts +114 -0
  35. package/src/table.ts +91 -0
  36. package/src/useTable.tsrx +34 -0
  37. package/types/index.d.ts +20 -0
  38. package/types/index.macos.d.ts +17 -0
  39. package/types/props.d.ts +96 -0
  40. package/types/table.d.ts +83 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Alec Larson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md CHANGED
@@ -1,4 +1,428 @@
1
- # @octane-xplat/table
1
+ # `@octane-xplat/table`
2
2
 
3
- This is a placeholder stub published to reserve the package name ahead of the
4
- first automated release. It has no supported API.
3
+ ```sh
4
+ pnpm add @octane-xplat/table
5
+ ```
6
+
7
+ Headless data grid for Octane xplat apps. The row model — sorting, filtering,
8
+ grouping, expanding, pagination, row selection, column ordering/visibility/
9
+ sizing — is `@tanstack/table-core` v9 (framework-agnostic, DOM-free), bound to
10
+ component rendering through TanStack Store atoms and `useSyncExternalStore`.
11
+ Rendering goes through shared ui primitives (`VirtualList` windowing on every
12
+ target), so one column/state contract covers web, iOS/Android, and macOS.
13
+
14
+ ```tsx
15
+ import { DataGrid } from '@octane-xplat/table'
16
+
17
+ const rows = [{ name: 'Passport', count: 1 }]
18
+ export function PackingGrid() {
19
+ return (
20
+ <DataGrid
21
+ className="packing-grid"
22
+ data={rows}
23
+ columns={[
24
+ { accessorKey: 'name', header: 'Name' },
25
+ { accessorKey: 'count', header: 'Count', size: 120 },
26
+ ]}
27
+ onRowPress={(row) => row.toggleSelected()}
28
+ />
29
+ )
30
+ }
31
+ ```
32
+
33
+ `DataGrid` is the default presentation: `vx-table-*` chrome (shared with the
34
+ bounded `Table` primitive), header rows from `table.getHeaderGroups()`,
35
+ virtualized body rows. Sortable headers toggle on press. The header and enabled
36
+ controls sit outside the body viewport, so they remain in place while rows scroll.
37
+ Give the grid a bounded height or a bounded flex parent.
38
+
39
+ ```css
40
+ /* App stylesheet; keep the body viewport bounded. */
41
+ .packing-grid {
42
+ height: 400px;
43
+ }
44
+ ```
45
+
46
+ For custom rendering keep the headless half and draw your own rows:
47
+
48
+ ```tsx
49
+ import { useTable } from '@octane-xplat/table'
50
+ import { Text } from '@octane-xplat/ui'
51
+
52
+ export function CustomRows() {
53
+ const { table, state } = useTable({
54
+ data: [{ name: 'Passport' }],
55
+ columns: [{ accessorKey: 'name', header: 'Name' }],
56
+ })
57
+ return (
58
+ <Text>
59
+ {table.getRowModel().rows.length} rows; {state.sorting.length} sorts
60
+ </Text>
61
+ )
62
+ }
63
+ ```
64
+
65
+ `createTable` (no hooks) constructs the same instance for headless use.
66
+
67
+ ```ts
68
+ import { createTable } from '@octane-xplat/table'
69
+
70
+ const table = createTable({
71
+ data: [{ name: 'Passport' }],
72
+ columns: [{ accessorKey: 'name', header: 'Name' }],
73
+ })
74
+ console.log(table.getRowModel().rows.length)
75
+ ```
76
+
77
+ ## Enable interaction controls
78
+
79
+ All controls are opt-in. No UI dependency is added; the leaf uses shared Xplat
80
+ primitives and the existing table-core state.
81
+
82
+ ```tsx
83
+ import { DataGrid } from '@octane-xplat/table'
84
+ import { Text } from '@octane-xplat/ui'
85
+
86
+ const orders = [{ id: 'order-1', name: 'Passport case', total: 12 }]
87
+
88
+ export function Orders() {
89
+ return (
90
+ <DataGrid
91
+ id="orders"
92
+ className="orders-grid"
93
+ data={orders}
94
+ getRowId={(order) => order.id}
95
+ columns={[
96
+ {
97
+ accessorKey: 'name',
98
+ header: 'Name',
99
+ size: 180,
100
+ minSize: 100,
101
+ maxSize: 300,
102
+ enableHiding: false,
103
+ },
104
+ { accessorKey: 'total', header: 'Total', size: 120 },
105
+ ]}
106
+ showGlobalFilter
107
+ showColumnFilters
108
+ showPagination
109
+ showRowSelection
110
+ showColumnSettings
111
+ resizableColumns
112
+ reorderableColumns
113
+ initialState={{ pagination: { pageIndex: 0, pageSize: 10 } }}
114
+ renderEmpty={() => <Text>No matching orders</Text>}
115
+ />
116
+ )
117
+ }
118
+ ```
119
+
120
+ ```css
121
+ .orders-grid {
122
+ height: 600px;
123
+ }
124
+ ```
125
+
126
+ Import `Text` from `@octane-xplat/ui`. Start with your application's `orders`
127
+ array and immutable updates; IDs must remain stable through filtering, sorting,
128
+ pages, and data replacement. The [maintained example](examples/interactive.tsrx)
129
+ includes concrete rows and a nonvisual probe.
130
+
131
+ ```ts
132
+ // Replace the data array when an order changes; preserve each ID.
133
+ const updatedOrders = orders.map((order) => ({ ...order, total: order.total + 1 }))
134
+ ```
135
+
136
+ | Prop | Behavior |
137
+ | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
138
+ | `showGlobalFilter` | Search across globally filterable columns. Changing the query resets the page index to zero. |
139
+ | `showColumnFilters` | Text fields for filterable leaf headings. Default filter is `includesString`, including numeric cells; custom column `filterFn` or `options.defaultColumn.filterFn` takes precedence. Custom range/date filters need a custom presentation via `useTable`. |
140
+ | `showPagination` | Previous/next, page count, and page-size buttons. `pageSizeOptions` defaults to `[10,25,50]`; invalid/nonpositive/noninteger entries are ignored. Empty data shows page zero of zero. Changing page size resets to page zero. |
141
+ | `showRowSelection` | Checkboxes for selectable rows, current-page select-all, total selected-ID count, and clear-all. IDs selected on other pages or outside the current filter remain selected. |
142
+ | `getRowLabel` | Accessible row-checkbox label; falls back to the row ID. |
143
+ | `renderSelectionActions(table)` | Caller-composed bulk actions next to the count/clear action; render only when `showRowSelection` is enabled. |
144
+ | `showColumnSettings` | A Columns button opens an inline panel with visibility checkboxes, earlier/later move buttons, and reset. `enableHiding:false` protects a column. Reset restores initial visibility/order/sizing. |
145
+ | `resizableColumns` | Fixed-width columns, live pan resize, and narrow/widen buttons in 10-dip increments, clamped to `minSize`/`maxSize`. Cancel restores the column's previous sizing override. |
146
+ | `reorderableColumns` | Separate pan handles commit a leaf-column move on release after crossing an adjacent column midpoint. Cancel preserves order. Use settings move buttons for keyboard/touch access without dragging. |
147
+ | `direction` | `ltr` by default; `rtl` reverses horizontal gesture deltas. Match the direction to your surrounding layout; this prop does not itself mirror the layout. |
148
+ | `options` | Core options such as state callbacks/atoms, selection predicates, server/manual row-model options, and defaults. Explicit `data`, `columns`, `state`, `initialState`, `features`, and `getRowId` props take precedence. |
149
+
150
+ Selection never activates `onRowPress`; the activation surface is a separate
151
+ sibling of the checkbox. `options.enableRowSelection` can be a predicate to
152
+ block specific rows. Select-all means the **current row-model page**, not every
153
+ record in a server dataset. Clear removes off-page IDs too; the caller decides
154
+ whether to prune IDs when records disappear. A missing ID remains in the count
155
+ but has no loaded row in `table.getSelectedRowModel()`; use the selection state
156
+ for server-side bulk actions that include unloaded records.
157
+
158
+ Page selection ignores disabled rows. When some eligible rows are selected, the
159
+ page control shows a dash; activating it selects the remaining eligible rows.
160
+ An empty or entirely disabled page disables that control. Give `getRowLabel` a
161
+ human-readable name, such as `row => row.original.name`, so a checkbox says
162
+ “Select Alice” rather than “Select a”. Web checkboxes support Space and Enter.
163
+
164
+ ### Row accessibility
165
+
166
+ The web presentation exposes a table with header/row/cell roles. Its row count
167
+ includes header rows plus the **filtered** data total. Body indices follow the
168
+ filtered, sorted order with the page offset, before VirtualList removes
169
+ unmounted rows. For example, page index 2 with page size 10 starts at data row 21
170
+ (`aria-rowindex=22` with one header row). Sorting changes positions, not row IDs.
171
+ Controls stay outside the table's semantic container.
172
+
173
+ Selection and activation controls carry a “Row 21 of 60” hint on native and web.
174
+ NativeScript has no mixed checkbox-state enum, so mixed selection uses the
175
+ spoken value “Partially selected” without claiming checked or unchecked.
176
+ AppKit exposes `AXCheckBox` with numeric off/on/mixed values (0/1/2) derived from
177
+ semantic state, independently of spoken text. Disabled controls reject actions.
178
+ These are host properties; assistive-technology navigation and speech still need
179
+ OS verification. This presentation does not implement spreadsheet-style arrow
180
+ navigation between cells.
181
+
182
+ ```tsx
183
+ export function SelectableOrders() {
184
+ return (
185
+ <DataGrid
186
+ data={orders}
187
+ getRowId={(order) => order.id}
188
+ columns={[{ accessorKey: 'name', header: 'Name' }]}
189
+ showRowSelection
190
+ options={{ enableRowSelection: (row) => row.original.total > 0 }}
191
+ />
192
+ )
193
+ }
194
+ ```
195
+
196
+ Widths are read from live `columnSizing` via `column.getSize()`. Enabling resize
197
+ or reorder makes all columns fixed-width so gesture distances and row/header
198
+ layout agree. Without these flags, unspecified widths retain the original
199
+ flexible distribution. Keep the visible columns within the grid viewport; this
200
+ version does not add a horizontally synchronized scroll owner.
201
+
202
+ ```tsx
203
+ export function SizedOrders() {
204
+ return (
205
+ <DataGrid
206
+ data={orders}
207
+ columns={[
208
+ { accessorKey: 'name', header: 'Name', size: 180, minSize: 100, maxSize: 300 },
209
+ { accessorKey: 'total', header: 'Total', size: 100 },
210
+ ]}
211
+ resizableColumns
212
+ reorderableColumns
213
+ />
214
+ )
215
+ }
216
+ ```
217
+
218
+ ## Feature surface
219
+
220
+ `dataGridFeatures` (the default set) enables every stock feature whose
221
+ machinery is DOM-free — sorting, column/global filtering, grouping +
222
+ aggregation, expanding, pagination, pinning, selection, column
223
+ ordering/visibility/sizing — plus the row-model factories and builtin
224
+ `sortFns`/`filterFns`/`aggregationFns` registries, which v9 keeps as separate
225
+ slots.
226
+
227
+ ```ts
228
+ import { createTable, dataGridFeatures } from '@octane-xplat/table'
229
+
230
+ const table = createTable({
231
+ features: dataGridFeatures,
232
+ data: orders,
233
+ columns: [{ accessorKey: 'name', header: 'Name' }],
234
+ })
235
+ table.setSorting([{ id: 'name', desc: false }])
236
+ console.log(table.getRowModel().rows)
237
+ ```
238
+
239
+ Not in the default set:
240
+
241
+ - `columnResizingFeature` — its drag handler installs `window` listeners;
242
+ resize-gesture UI is a platform-leaf concern.
243
+ - `cellSelectionFeature` / `cellSpanningFeature` — range selection is
244
+ spreadsheet-shaped; revisit per platform.
245
+
246
+ Pass `features` to add slots (e.g.
247
+ `features: { columnResizingFeature }` on a web-only surface).
248
+
249
+ ```tsx
250
+ // Grid.web.tsx — upstream's resizing feature is browser-only.
251
+ import { columnResizingFeature } from '@tanstack/table-core'
252
+ import { DataGrid } from '@octane-xplat/table'
253
+
254
+ export function WebGrid() {
255
+ return (
256
+ <DataGrid
257
+ data={[{ name: 'Passport' }]}
258
+ columns={[{ accessorKey: 'name', header: 'Name' }]}
259
+ features={{ columnResizingFeature }}
260
+ />
261
+ )
262
+ }
263
+ ```
264
+
265
+ ## Controlled state
266
+
267
+ State slices live in `table.baseAtoms` (internal) with `table.atoms` as the
268
+ derived readonly view; `table.store` is the flat `TableState` store the
269
+ adapter subscribes to. For a custom `useTable` presentation, pass `state` plus `atoms` (external writable
270
+ atoms) or the corresponding change callbacks. DataGrid now exposes those through
271
+ `options`. A `state` snapshot alone freezes that slice; subscribing alone does
272
+ not make a read-only snapshot writable.
273
+
274
+ ```tsx
275
+ import { useState } from 'octane'
276
+ import type { DataGridFeatures, TableState } from '@octane-xplat/table'
277
+
278
+ // useTable and Text are imported in CustomRows above. Call within a component.
279
+ export function ControlledRows() {
280
+ const [sorting, setSorting] = useState<TableState<DataGridFeatures>['sorting']>([])
281
+ const { table } = useTable({
282
+ data: orders,
283
+ columns: [{ accessorKey: 'name', header: 'Name' }],
284
+ state: { sorting },
285
+ onSortingChange: setSorting,
286
+ })
287
+ return <Text>{table.getRowModel().rows.length} rows</Text>
288
+ }
289
+ ```
290
+
291
+ For example, a caller-owned sorting slice uses core updater callbacks:
292
+
293
+ ```tsx
294
+ import { useState } from 'octane'
295
+ import { DataGrid, type DataGridFeatures, type TableState } from '@octane-xplat/table'
296
+
297
+ export function SortedOrders() {
298
+ const [sorting, setSorting] = useState<TableState<DataGridFeatures>['sorting']>([])
299
+ return (
300
+ <DataGrid
301
+ data={orders}
302
+ columns={[{ accessorKey: 'name', header: 'Name' }]}
303
+ state={{ sorting }}
304
+ options={{ onSortingChange: setSorting }}
305
+ />
306
+ )
307
+ }
308
+ ```
309
+
310
+ Import `useState` from `octane`; the explicit state type keeps an empty initial
311
+ array from becoming `never[]`.
312
+
313
+ ```tsx
314
+ import { useState } from 'octane'
315
+ import type { DataGridFeatures, TableState } from '@octane-xplat/table'
316
+
317
+ export function SortCount() {
318
+ const [sorting] = useState<TableState<DataGridFeatures>['sorting']>([])
319
+ return <Text>{sorting.length}</Text>
320
+ }
321
+ ```
322
+
323
+ Selection uses the same ownership pattern. The callback receives either a new
324
+ value or a function that updates the previous value; `useState` accepts both:
325
+
326
+ ```tsx
327
+ const [rowSelection, setRowSelection] =
328
+ useState<TableState<DataGridFeatures>['rowSelection']>({})
329
+ <DataGrid data={rows} columns={columns} getRowId={row => row.id}
330
+ showRowSelection getRowLabel={row => row.original.name}
331
+ state={{ rowSelection }} options={{ onRowSelectionChange: setRowSelection }} />
332
+ ```
333
+
334
+ Click a checkbox and the selected count should update. Filtering, changing pages,
335
+ and replacing data keep selection by ID. Clear selection also removes IDs for
336
+ records that are currently unloaded. The [interactive example](examples/interactive.tsrx)
337
+ uses this caller-owned selection. A snapshot without an update callback remains
338
+ read-only; DataGrid does not silently take ownership.
339
+
340
+ For server pages, supply already paged `data`, `state.pagination`, and
341
+ `options={{ manualPagination: true, rowCount, onPaginationChange }}`. The callback
342
+ receives the core updater and must publish the new controlled state plus fetch
343
+ that page. Unknown totals use `pageCount: -1`; next stays enabled, so the caller
344
+ must own terminal/cursor behavior. Supply `rowCount` for accessible server totals;
345
+ without it, the table reports an unknown row count (`aria-rowcount=-1`) and hints
346
+ say “Row 21” without a total. `pageCount` alone does not determine the exact final
347
+ row count. Filtering and sorting of manual server data are also caller-owned;
348
+ set the corresponding core manual options to avoid transforming it twice.
349
+ This is not an automatic server fetcher.
350
+
351
+ ```tsx
352
+ import { useState } from 'octane'
353
+ import { DataGrid, type DataGridFeatures, type TableState } from '@octane-xplat/table'
354
+
355
+ // The parent fetches pageRows whenever pagination changes.
356
+ export function ServerPage({
357
+ pageRows,
358
+ rowCount,
359
+ requestPage,
360
+ }: {
361
+ pageRows: { id: string; name: string }[]
362
+ rowCount: number
363
+ requestPage: (pageIndex: number, pageSize: number) => void
364
+ }) {
365
+ const [pagination, setPagination] = useState<TableState<DataGridFeatures>['pagination']>({
366
+ pageIndex: 0,
367
+ pageSize: 10,
368
+ })
369
+ return (
370
+ <DataGrid
371
+ data={pageRows}
372
+ columns={[{ accessorKey: 'name', header: 'Name' }]}
373
+ showPagination
374
+ state={{ pagination }}
375
+ options={{
376
+ manualPagination: true,
377
+ rowCount,
378
+ onPaginationChange: (updater) => {
379
+ const next = typeof updater === 'function' ? updater(pagination) : updater
380
+ setPagination(next)
381
+ requestPage(next.pageIndex, next.pageSize)
382
+ },
383
+ }}
384
+ />
385
+ )
386
+ }
387
+ ```
388
+
389
+ ## Boundaries
390
+
391
+ - Vertical virtualization only (VirtualList); no horizontal column
392
+ virtualization or sticky column-pinning layout. Headers stay outside vertical
393
+ body scrolling; that does not pin them against an outer page scroll.
394
+ - `Table` in `@octane-xplat/ui` stays the bounded, unvirtualized
395
+ presentational tier — DataGrid is the data-grid tier above it.
396
+
397
+ ## Verification and remaining parity gaps
398
+
399
+ Run `pnpm --filter @octane-xplat/table typecheck`, `test`, `test:native`, `build`, and
400
+ `test:packed`. The source typecheck covers web and the iOS native resolution lane;
401
+ the native build uses the same shared controls for iOS/Android. Run the fixture:
402
+
403
+ ```sh
404
+ pnpm probe run packages/table/examples/interactive.tsrx --target web --deps @octane-xplat/table
405
+ pnpm probe run packages/table/examples/interactive.tsrx --target ios --device YOUR_BOOTED_SIMULATOR_UDID --deps @octane-xplat/table
406
+ pnpm probe run packages/table/examples/geometry.tsrx --target macos --deps @octane-xplat/table
407
+ ```
408
+
409
+ The geometry fixture isolates bounded row sorting and column resize/reorder from
410
+ filter/pager input. Its AppKit run measures native frames and retained row order.
411
+ The iOS and Android selection fixtures currently hit an unrelated Unicode
412
+ property-regex parse failure before assertions; the direct VirtualList iOS geometry case passes. The full
413
+ AppKit fixture has an unresolved filter-input timeout.
414
+
415
+ Use a real booted simulator ID from `pnpm probe doctor`. Results are handler
416
+ or pointer dispatch, not proof of OS input, hit-testing, or screen-reader
417
+ navigation. See the [audit implementation record](../../docs/notes/astryx-parity-table.md#implementation-follow-through)
418
+ for the actual targets/results and known probe blockers.
419
+
420
+ Still deferred: grouped section headers/detail/tree controls, frozen-column
421
+ layout, PowerSearch operator/date/multi-select filter UI, richer multi-sort UX,
422
+ column virtualization, cell editing, and OS screen-reader/input verification.
423
+ The maintained [selection fixture](examples/selection.tsrx) checks the real
424
+ VirtualList, caller state, eligible page scope, mixed controls, and a windowed
425
+ row ordinal. Chromium and AppKit pass its handler-dispatch and host-property
426
+ checks; iOS/Android object-driver checks cover shared controls but are not
427
+ on-device accessibility evidence. These limits do not remove the
428
+ underlying headless features.
@@ -0,0 +1,237 @@
1
+ import { dragTarget, moveColumn, resizedWidth } from "./grid-layout.js";
2
+ import { defineUniversalComponent, hookSlots, universalChildren, universalComponent, universalPlan, universalProps, universalValue, useRef } from "@nativescript-community/octane";
3
+ import { HStack, Pressable, Text, View } from "@octane-xplat/ui";
4
+ //#region src/ColumnDrag.tsrx
5
+ var _h$0 = /* @__PURE__ */ Symbol(/* @__PURE__ */ hookSlots(1));
6
+ var __octaneUniversalPlan0 = universalPlan("nativescript", {
7
+ "kind": "text",
8
+ "value": "↔"
9
+ });
10
+ var __octaneUniversalPlan1 = universalPlan("nativescript", {
11
+ "kind": "slot",
12
+ "slot": 0
13
+ });
14
+ var __octaneUniversalPlan2 = universalPlan("nativescript", {
15
+ "kind": "slot",
16
+ "slot": 0
17
+ });
18
+ var __octaneUniversalPlan3 = universalPlan("nativescript", {
19
+ "kind": "text",
20
+ "value": "−"
21
+ });
22
+ var __octaneUniversalPlan4 = universalPlan("nativescript", {
23
+ "kind": "slot",
24
+ "slot": 0
25
+ });
26
+ var __octaneUniversalPlan5 = universalPlan("nativescript", {
27
+ "kind": "text",
28
+ "value": "│"
29
+ });
30
+ var __octaneUniversalPlan6 = universalPlan("nativescript", {
31
+ "kind": "slot",
32
+ "slot": 0
33
+ });
34
+ var __octaneUniversalPlan7 = universalPlan("nativescript", {
35
+ "kind": "text",
36
+ "value": "+"
37
+ });
38
+ var __octaneUniversalPlan8 = universalPlan("nativescript", {
39
+ "kind": "slot",
40
+ "slot": 0
41
+ });
42
+ var __octaneUniversalPlan9 = universalPlan("nativescript", {
43
+ "kind": "range",
44
+ "children": [
45
+ {
46
+ "kind": "slot",
47
+ "slot": 0
48
+ },
49
+ {
50
+ "kind": "slot",
51
+ "slot": 1
52
+ },
53
+ {
54
+ "kind": "slot",
55
+ "slot": 2
56
+ }
57
+ ]
58
+ });
59
+ var __octaneUniversalPlan10 = universalPlan("nativescript", {
60
+ "kind": "range",
61
+ "children": [{
62
+ "kind": "slot",
63
+ "slot": 0
64
+ }, {
65
+ "kind": "slot",
66
+ "slot": 1
67
+ }]
68
+ });
69
+ var __octaneUniversalPlan11 = universalPlan("nativescript", {
70
+ "kind": "slot",
71
+ "slot": 0
72
+ });
73
+ var ColumnDrag = defineUniversalComponent("nativescript", function ColumnDrag(props) {
74
+ props.state;
75
+ const drag = useRef(null, _h$0);
76
+ const begin = () => {
77
+ drag.current = {
78
+ size: props.column.getSize(),
79
+ sizing: { ...props.table.store.get().columnSizing },
80
+ order: props.table.getAllLeafColumns().map((column) => column.id),
81
+ columns: props.table.getVisibleLeafColumns().map((column) => {
82
+ const size = column.getSize();
83
+ return {
84
+ id: column.id,
85
+ getSize: () => size
86
+ };
87
+ })
88
+ };
89
+ };
90
+ const onPan = (event, resize) => {
91
+ if (event.state === "began") {
92
+ begin();
93
+ return;
94
+ }
95
+ const start = drag.current;
96
+ if (!start) return;
97
+ if (event.state === "cancelled") {
98
+ if (resize) props.table.setColumnSizing((old) => {
99
+ const next = { ...old };
100
+ if (start.sizing[props.column.id] == null) delete next[props.column.id];
101
+ else next[props.column.id] = start.sizing[props.column.id];
102
+ return next;
103
+ });
104
+ drag.current = null;
105
+ return;
106
+ }
107
+ const dx = event.dx * (props.direction === "rtl" ? -1 : 1);
108
+ if (resize && Number.isFinite(dx)) props.table.setColumnSizing((old) => ({
109
+ ...old,
110
+ [props.column.id]: resizedWidth(props.column, start.size + dx)
111
+ }));
112
+ if (event.state === "ended") {
113
+ if (!resize) {
114
+ const target = dragTarget(start.columns, props.column.id, dx);
115
+ props.table.setColumnOrder(moveColumn(start.order, props.column.id, target));
116
+ }
117
+ drag.current = null;
118
+ }
119
+ };
120
+ const adjust = (delta) => props.table.setColumnSizing((old) => ({
121
+ ...old,
122
+ [props.column.id]: resizedWidth(props.column, props.column.getSize() + delta)
123
+ }));
124
+ return universalValue(__octaneUniversalPlan11, [universalComponent("nativescript", HStack, universalProps([[
125
+ "set",
126
+ "className",
127
+ "vx-datagrid-handles"
128
+ ]], universalChildren("nativescript", () => {
129
+ return universalValue(__octaneUniversalPlan10, [props.reorder ? universalValue(__octaneUniversalPlan2, [universalComponent("nativescript", View, universalProps([
130
+ [
131
+ "set",
132
+ "id",
133
+ props.id ? `${props.id}-drag` : void 0
134
+ ],
135
+ [
136
+ "set",
137
+ "className",
138
+ "vx-datagrid-handle"
139
+ ],
140
+ [
141
+ "set",
142
+ "onPan",
143
+ (event) => onPan(event, false)
144
+ ],
145
+ [
146
+ "set",
147
+ "accessibilityLabel",
148
+ `Drag ${props.column.id} to reorder`
149
+ ]
150
+ ], universalChildren("nativescript", () => {
151
+ return universalValue(__octaneUniversalPlan1, [universalComponent("nativescript", Text, universalProps([], universalChildren("nativescript", () => {
152
+ return universalValue(__octaneUniversalPlan0, []);
153
+ })))]);
154
+ })))]) : null, props.resize ? universalValue(__octaneUniversalPlan9, [
155
+ universalComponent("nativescript", Pressable, universalProps([
156
+ [
157
+ "set",
158
+ "id",
159
+ props.id ? `${props.id}-narrow` : void 0
160
+ ],
161
+ [
162
+ "set",
163
+ "className",
164
+ "vx-datagrid-handle"
165
+ ],
166
+ [
167
+ "set",
168
+ "onPress",
169
+ () => adjust(-10)
170
+ ],
171
+ [
172
+ "set",
173
+ "accessibilityLabel",
174
+ `Narrow ${props.column.id}`
175
+ ]
176
+ ], universalChildren("nativescript", () => {
177
+ return universalValue(__octaneUniversalPlan4, [universalComponent("nativescript", Text, universalProps([], universalChildren("nativescript", () => {
178
+ return universalValue(__octaneUniversalPlan3, []);
179
+ })))]);
180
+ }))),
181
+ universalComponent("nativescript", View, universalProps([
182
+ [
183
+ "set",
184
+ "id",
185
+ props.id ? `${props.id}-resize` : void 0
186
+ ],
187
+ [
188
+ "set",
189
+ "className",
190
+ "vx-datagrid-handle"
191
+ ],
192
+ [
193
+ "set",
194
+ "onPan",
195
+ (event) => onPan(event, true)
196
+ ],
197
+ [
198
+ "set",
199
+ "accessibilityLabel",
200
+ `Drag to resize ${props.column.id}`
201
+ ]
202
+ ], universalChildren("nativescript", () => {
203
+ return universalValue(__octaneUniversalPlan6, [universalComponent("nativescript", Text, universalProps([], universalChildren("nativescript", () => {
204
+ return universalValue(__octaneUniversalPlan5, []);
205
+ })))]);
206
+ }))),
207
+ universalComponent("nativescript", Pressable, universalProps([
208
+ [
209
+ "set",
210
+ "id",
211
+ props.id ? `${props.id}-widen` : void 0
212
+ ],
213
+ [
214
+ "set",
215
+ "className",
216
+ "vx-datagrid-handle"
217
+ ],
218
+ [
219
+ "set",
220
+ "onPress",
221
+ () => adjust(10)
222
+ ],
223
+ [
224
+ "set",
225
+ "accessibilityLabel",
226
+ `Widen ${props.column.id}`
227
+ ]
228
+ ], universalChildren("nativescript", () => {
229
+ return universalValue(__octaneUniversalPlan8, [universalComponent("nativescript", Text, universalProps([], universalChildren("nativescript", () => {
230
+ return universalValue(__octaneUniversalPlan7, []);
231
+ })))]);
232
+ })))
233
+ ]) : null]);
234
+ })))]);
235
+ }, { "module": "@nativescript-community/octane" });
236
+ //#endregion
237
+ export { ColumnDrag };