@stonecrop/atable 0.13.14 → 0.14.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.
@@ -12,6 +12,12 @@ import InsertBelowIcon from './stonecrop-ui-icon-insert-below.svg?raw'
12
12
  import MoveIcon from './stonecrop-ui-icon-move.svg?raw'
13
13
  import OpenIcon from './stonecrop-ui-icon-open.svg?raw'
14
14
 
15
+ // Directional move icons are inline (no dedicated SVG asset): simple up/down chevrons.
16
+ const MoveUpIcon =
17
+ '<svg viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M8 13V3.5M4.5 7 8 3.5 11.5 7" stroke-linecap="round" stroke-linejoin="round"/></svg>'
18
+ const MoveDownIcon =
19
+ '<svg viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M8 3v9.5M4.5 9 8 12.5 11.5 9" stroke-linecap="round" stroke-linejoin="round"/></svg>'
20
+
15
21
  export { AddIcon, DeleteIcon, DuplicateIcon, InsertAboveIcon, InsertBelowIcon, MoveIcon, OpenIcon }
16
22
 
17
23
  /**
@@ -26,5 +32,7 @@ export const actionIcons: Record<string, string> = {
26
32
  insertAbove: InsertAboveIcon,
27
33
  insertBelow: InsertBelowIcon,
28
34
  move: MoveIcon,
35
+ moveUp: MoveUpIcon,
36
+ moveDown: MoveDownIcon,
29
37
  open: OpenIcon,
30
38
  }
package/src/index.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { App } from 'vue'
2
2
 
3
3
  import ACell from './components/ACell.vue'
4
- import AExpansionRow from './components/AExpansionRow.vue'
5
4
  import AGanttCell from './components/AGanttCell.vue'
6
5
  import ARow from './components/ARow.vue'
7
6
  import ARowActions from './components/ARowActions.vue'
@@ -34,7 +33,6 @@ export {
34
33
  */
35
34
  function install(app: App /* options */) {
36
35
  app.component('ACell', ACell)
37
- app.component('AExpansionRow', AExpansionRow)
38
36
  app.component('AGanttCell', AGanttCell)
39
37
  app.component('ARow', ARow)
40
38
  app.component('ARowActions', ARowActions)
@@ -47,7 +45,6 @@ function install(app: App /* options */) {
47
45
 
48
46
  export {
49
47
  ACell,
50
- AExpansionRow,
51
48
  AGanttCell,
52
49
  ARow,
53
50
  ARowActions,
@@ -7,12 +7,12 @@ import type { TableColumn } from './types'
7
7
  *
8
8
  * Fields are excluded when:
9
9
  * - `hidden: true` — field should not be visible in any view
10
- * - no `fieldtype` — non-scalar entry (nested table or fieldset), has no column equivalent
10
+ * - no `component` — non-scalar entry (nested table or fieldset), no column equivalent
11
11
  *
12
12
  * `fieldname` is renamed to `name`; `hidden` is stripped. All other `ColumnSchema` properties
13
13
  * spread through automatically.
14
14
  *
15
- * For `fieldtype: 'Link'` fields without an explicit `cellComponent`:
15
+ * For link fields (those carrying `doctype`) without an explicit `cellComponent`:
16
16
  * - `linkDoctype` is set from the field's `doctype` property (used by ACell's async resolver).
17
17
  * - A synchronous `format` function is added (unless the field already has one) that handles
18
18
  * both bare ID strings and pre-resolved `{ id, displayText }` objects.
@@ -21,16 +21,14 @@ import type { TableColumn } from './types'
21
21
  */
22
22
  export function schemaToColumns(schema: ColumnSchema[]): TableColumn[] {
23
23
  return schema
24
- .filter(f => !f.hidden && f.fieldtype)
24
+ .filter(f => !f.hidden && f.component)
25
25
  .map(({ fieldname, hidden: _hidden, ...rest }) => {
26
26
  const col: TableColumn = Object.assign({ name: fieldname }, rest)
27
27
 
28
- // Link fields: store the linked doctype for async resolution by ACell,
29
- // and add a sync format that handles pre-resolved AFormLinkValue objects.
30
- if (rest.fieldtype === 'Link' && !rest.cellComponent) {
31
- const fields = rest as Record<string, unknown>
32
- const linkedDoctype = typeof fields.doctype === 'string' ? fields.doctype : undefined
33
- if (linkedDoctype) col.linkDoctype = linkedDoctype
28
+ // Link fields: store the linked doctype for async resolution by ACell, and add a sync
29
+ // format that handles pre-resolved AFormLinkValue objects.
30
+ if (rest.doctype && !rest.cellComponent) {
31
+ col.linkDoctype = rest.doctype
34
32
 
35
33
  if (!rest.format) {
36
34
  col.format = (v: any): string => {
@@ -1,4 +1,5 @@
1
1
  import { defineStore } from 'pinia'
2
+ import { componentCategory } from '@stonecrop/schema'
2
3
  import { type CSSProperties, computed, ref } from 'vue'
3
4
 
4
5
  import type {
@@ -127,7 +128,12 @@ function applyFilter(cellValue: any, filter: FilterState, column: TableColumn):
127
128
  }
128
129
  }
129
130
 
130
- function getIndent(colIndex: number, indentLevel?: number): string {
131
+ /**
132
+ * Compute the CSS indentation for a tree-table cell. Pure helper (captures no store state);
133
+ * imported directly by cell components rather than exposed on the store's public return.
134
+ * @internal
135
+ */
136
+ export function getIndent(colIndex: number, indentLevel?: number): string {
131
137
  if (indentLevel && colIndex === 0 && indentLevel > 0) {
132
138
  return `${indentLevel}ch`
133
139
  } else {
@@ -509,16 +515,13 @@ export const createTableStore = (initData: {
509
515
  const format = column.format
510
516
 
511
517
  if (!format) {
512
- switch (column.fieldtype) {
513
- case 'Check':
514
- return value ? '✓' : '✗'
515
- case 'Date':
516
- return value != null ? new Date(String(value)).toLocaleDateString() : value
517
- case 'Datetime':
518
- return value != null ? new Date(String(value)).toLocaleString() : value
519
- default:
520
- return value
521
- }
518
+ // Default cell formatting comes from the component's category; anything without an
519
+ // opinion (including an unknown component) renders the raw value.
520
+ const category = componentCategory(column.component)
521
+ if (category === 'boolean') return value ? '✓' : '✗'
522
+ if (category === 'date') return value != null ? new Date(String(value)).toLocaleDateString() : value
523
+ if (category === 'datetime') return value != null ? new Date(String(value)).toLocaleString() : value
524
+ return value
522
525
  }
523
526
 
524
527
  if (typeof format === 'function') {
@@ -922,7 +925,6 @@ export const createTableStore = (initData: {
922
925
  getFormattedValue,
923
926
  getHandlesForBar,
924
927
  getHeaderCellStyle,
925
- getIndent,
926
928
  getRowExpandSymbol,
927
929
  insertRowAbove,
928
930
  insertRowBelow,
@@ -8,7 +8,7 @@ import { createTableStore } from '../stores/table'
8
8
  /**
9
9
  * Runtime column definition for ATable.
10
10
  *
11
- * Extends `ColumnSchema` from `@stonecrop/schema` — all authoring properties (`label`, `fieldtype`, `width`,
11
+ * Extends `ColumnSchema` from `@stonecrop/schema` — all authoring properties (`label`, `component`, `width`,
12
12
  * `pinned`, filter config, cell/modal component names, etc.) are inherited. The overrides
13
13
  * below widen three properties for runtime use (live functions, broader alignment values)
14
14
  * and add two runtime-only additions (`mask`, `originalIndex`).
@@ -45,7 +45,7 @@ export interface TableColumn extends Omit<ColumnSchema, 'fieldname' | 'hidden' |
45
45
  mask?: (value: any) => any
46
46
 
47
47
  /**
48
- * For `fieldtype: 'Link'` columns: the target doctype slug used by the `linkResolver`
48
+ * For link columns (those carrying `doctype`): the target doctype slug used by the `linkResolver`
49
49
  * to look up display text for bare ID values. Set automatically by `schemaToColumns`
50
50
  * from the field's `doctype` property.
51
51
  */
@@ -83,7 +83,8 @@ export interface CellContext {
83
83
  * Row action type identifiers.
84
84
  * @public
85
85
  */
86
- export type RowActionType = 'open' | 'add' | 'delete' | 'duplicate' | 'insertAbove' | 'insertBelow' | 'move'
86
+ export type RowActionType =
87
+ 'open' | 'add' | 'delete' | 'duplicate' | 'insertAbove' | 'insertBelow' | 'move' | 'moveUp' | 'moveDown'
87
88
 
88
89
  /**
89
90
  * Options for configuring individual row actions.
@@ -115,6 +116,16 @@ export interface RowActionOptions {
115
116
  * @returns void or false to prevent default behavior
116
117
  */
117
118
  handler?: (rowIndex: number, store: ReturnType<typeof createTableStore>) => void | boolean
119
+
120
+ /**
121
+ * Per-row predicate to disable this action for specific rows (e.g. a lock-aware delete, or
122
+ * move-up on the first row). Returns true to render the action disabled. Evaluated reactively
123
+ * against the store, so it updates as rows change.
124
+ *
125
+ * @param rowIndex - The index of the row
126
+ * @param store - The table store instance
127
+ */
128
+ disabled?: (rowIndex: number, store: ReturnType<typeof createTableStore>) => boolean
118
129
  }
119
130
 
120
131
  /**
@@ -163,6 +174,8 @@ export interface RowActionsConfig {
163
174
  insertAbove?: boolean | RowActionOptions
164
175
  insertBelow?: boolean | RowActionOptions
165
176
  move?: boolean | RowActionOptions
177
+ moveUp?: boolean | RowActionOptions
178
+ moveDown?: boolean | RowActionOptions
166
179
  }
167
180
  }
168
181
 
@@ -1,105 +0,0 @@
1
- <template>
2
- <tr v-bind="$attrs" ref="rowEl" :tabindex="tabIndex" class="expandable-row">
3
- <td :tabIndex="-1" class="row-index" @click="store.toggleRowExpand(rowIndex)">
4
- {{ rowExpandSymbol }}
5
- </td>
6
- <slot name="row" />
7
- </tr>
8
- <tr v-if="store.display[rowIndex].expanded" ref="rowExpanded" :tabindex="tabIndex" class="expanded-row">
9
- <td :tabIndex="-1" :colspan="store.columns.length + 1" class="expanded-row-content">
10
- <slot name="content" />
11
- </td>
12
- </tr>
13
- </template>
14
-
15
- <script setup lang="ts">
16
- import { defaultKeypressHandlers, type KeypressHandlers, useKeyboardNav } from '@stonecrop/utilities'
17
- import { computed, useTemplateRef } from 'vue'
18
-
19
- import { createTableStore } from '../stores/table'
20
-
21
- const {
22
- rowIndex,
23
- store,
24
- tabIndex = -1,
25
- addNavigation = defaultKeypressHandlers,
26
- } = defineProps<{
27
- rowIndex: number
28
- store: ReturnType<typeof createTableStore>
29
- tabIndex?: number
30
- addNavigation?: boolean | KeypressHandlers
31
- }>()
32
-
33
- const rowRef = useTemplateRef<HTMLTableRowElement>('rowEl')
34
- // const expandedRowRef = useTemplateRef<HTMLDivElement>('rowExpanded')
35
-
36
- const rowExpandSymbol = computed(() => {
37
- return store.display[rowIndex].expanded ? '▼' : '►'
38
- })
39
-
40
- if (addNavigation) {
41
- const handlers: KeypressHandlers = {
42
- 'keydown.control.g': (event: KeyboardEvent) => {
43
- event.stopPropagation()
44
- event.preventDefault()
45
- store.toggleRowExpand(rowIndex)
46
- },
47
- }
48
-
49
- if (typeof addNavigation === 'object') {
50
- Object.assign(handlers, addNavigation)
51
- }
52
-
53
- useKeyboardNav([
54
- {
55
- selectors: rowRef,
56
- handlers: handlers,
57
- },
58
- ])
59
- }
60
- </script>
61
-
62
- <style>
63
- @import url('@stonecrop/themes/default.css');
64
-
65
- .row-index {
66
- color: var(--sc-header-text-color);
67
- font-weight: bold;
68
- text-align: center;
69
- user-select: none;
70
- width: 2ch;
71
- display: flex;
72
- align-items: center;
73
- justify-content: center;
74
- }
75
-
76
- .expandable-row {
77
- border-top: 1px solid var(--sc-row-border-color);
78
- height: var(--sc-atable-row-height);
79
- }
80
-
81
- .expandable-row > td:first-child {
82
- border-left: 4px solid var(--sc-row-border-color);
83
- }
84
-
85
- .expanded-row {
86
- border-left: 2px solid var(--sc-row-border-color);
87
- }
88
-
89
- .expandable-row:last-child {
90
- border-bottom: 1px solid var(--sc-row-border-color);
91
- }
92
-
93
- .expanded-row-content {
94
- border-top: 1px solid var(--sc-row-border-color);
95
- padding: 1.5rem;
96
- }
97
- </style>
98
-
99
- <style scoped>
100
- .expandable-row.changed-row-gradient:has(td.cell-modified) {
101
- --cell-color-start: color-mix(in srgb, var(--sc-cell-changed-color), #fff 20%);
102
- --cell-color-end: color-mix(in srgb, var(--sc-cell-changed-color), #fff 60%);
103
- background: linear-gradient(90deg, var(--cell-color-start), var(--cell-color-end));
104
- }
105
- </style>