@shwfed/config 2.3.1 → 2.3.3

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 (33) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +2 -0
  3. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.d.vue.ts +2 -0
  4. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.vue.d.ts +2 -0
  5. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.d.vue.ts +2 -0
  6. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.vue.d.ts +2 -0
  7. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/schema.d.ts +2 -0
  8. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/config.d.vue.ts +2 -0
  9. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/config.vue.d.ts +2 -0
  10. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/schema.d.ts +2 -0
  11. package/dist/runtime/components/table/columns/2026-05-21/com.shwfed.table.column.drag-handle/config.d.vue.ts +10 -0
  12. package/dist/runtime/components/table/columns/2026-05-21/com.shwfed.table.column.drag-handle/config.vue +10 -0
  13. package/dist/runtime/components/table/columns/2026-05-21/com.shwfed.table.column.drag-handle/config.vue.d.ts +10 -0
  14. package/dist/runtime/components/table/columns/2026-05-21/com.shwfed.table.column.drag-handle/runtime.d.vue.ts +9 -0
  15. package/dist/runtime/components/table/columns/2026-05-21/com.shwfed.table.column.drag-handle/runtime.vue +68 -0
  16. package/dist/runtime/components/table/columns/2026-05-21/com.shwfed.table.column.drag-handle/runtime.vue.d.ts +9 -0
  17. package/dist/runtime/components/table/columns/2026-05-21/com.shwfed.table.column.drag-handle/schema.d.ts +17 -0
  18. package/dist/runtime/components/table/columns/2026-05-21/com.shwfed.table.column.drag-handle/schema.js +31 -0
  19. package/dist/runtime/components/table/config.d.vue.ts +2 -2
  20. package/dist/runtime/components/table/config.vue +53 -4
  21. package/dist/runtime/components/table/config.vue.d.ts +2 -2
  22. package/dist/runtime/components/table/index.d.vue.ts +2 -0
  23. package/dist/runtime/components/table/index.vue +110 -4
  24. package/dist/runtime/components/table/index.vue.d.ts +2 -0
  25. package/dist/runtime/components/table/schema.d.ts +4 -0
  26. package/dist/runtime/components/table/schema.js +8 -1
  27. package/dist/runtime/components/table/utils/reorder.d.ts +10 -0
  28. package/dist/runtime/components/table/utils/reorder.js +8 -0
  29. package/dist/runtime/components/table/utils/row-key.d.ts +11 -0
  30. package/dist/runtime/components/table/utils/row-key.js +30 -0
  31. package/dist/runtime/components/table/utils/row-reorder.d.ts +12 -0
  32. package/dist/runtime/components/table/utils/row-reorder.js +162 -0
  33. package/package.json +1 -1
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shwfed",
3
3
  "configKey": "shwfed",
4
- "version": "2.3.1",
4
+ "version": "2.3.3",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -107,6 +107,8 @@ const module$1 = defineNuxtModule({
107
107
  "@atlaskit/pragmatic-drag-and-drop/combine",
108
108
  "@atlaskit/pragmatic-drag-and-drop/element/adapter",
109
109
  "@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item",
110
+ "@atlaskit/pragmatic-drag-and-drop/element/preserve-offset-on-source",
111
+ "@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview",
110
112
  "vaul-vue",
111
113
  "dot-prop",
112
114
  "@internationalized/date",
@@ -131,6 +131,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
131
131
  }[];
132
132
  readonly readonly?: string | undefined;
133
133
  } | undefined;
134
+ readonly rowKey?: string | undefined;
134
135
  readonly cellStyle?: string | undefined;
135
136
  readonly pagination?: {
136
137
  readonly left?: readonly [{
@@ -306,6 +307,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
306
307
  }[];
307
308
  readonly readonly?: string | undefined;
308
309
  } | undefined;
310
+ readonly rowKey?: string | undefined;
309
311
  readonly cellStyle?: string | undefined;
310
312
  readonly pagination?: {
311
313
  readonly left?: readonly [{
@@ -131,6 +131,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
131
131
  }[];
132
132
  readonly readonly?: string | undefined;
133
133
  } | undefined;
134
+ readonly rowKey?: string | undefined;
134
135
  readonly cellStyle?: string | undefined;
135
136
  readonly pagination?: {
136
137
  readonly left?: readonly [{
@@ -306,6 +307,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
306
307
  }[];
307
308
  readonly readonly?: string | undefined;
308
309
  } | undefined;
310
+ readonly rowKey?: string | undefined;
309
311
  readonly cellStyle?: string | undefined;
310
312
  readonly pagination?: {
311
313
  readonly left?: readonly [{
@@ -126,6 +126,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
126
126
  }[];
127
127
  readonly readonly?: string | undefined;
128
128
  } | undefined;
129
+ readonly rowKey?: string | undefined;
129
130
  readonly cellStyle?: string | undefined;
130
131
  readonly pagination?: {
131
132
  readonly left?: readonly [{
@@ -301,6 +302,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
301
302
  }[];
302
303
  readonly readonly?: string | undefined;
303
304
  } | undefined;
305
+ readonly rowKey?: string | undefined;
304
306
  readonly cellStyle?: string | undefined;
305
307
  readonly pagination?: {
306
308
  readonly left?: readonly [{
@@ -126,6 +126,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
126
126
  }[];
127
127
  readonly readonly?: string | undefined;
128
128
  } | undefined;
129
+ readonly rowKey?: string | undefined;
129
130
  readonly cellStyle?: string | undefined;
130
131
  readonly pagination?: {
131
132
  readonly left?: readonly [{
@@ -301,6 +302,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
301
302
  }[];
302
303
  readonly readonly?: string | undefined;
303
304
  } | undefined;
305
+ readonly rowKey?: string | undefined;
304
306
  readonly cellStyle?: string | undefined;
305
307
  readonly pagination?: {
306
308
  readonly left?: readonly [{
@@ -141,6 +141,7 @@ export declare function schema(configure: (env: Environment) => void, _blockRef:
141
141
  }[];
142
142
  readonly readonly?: string | undefined;
143
143
  } | undefined;
144
+ readonly rowKey?: string | undefined;
144
145
  readonly cellStyle?: string | undefined;
145
146
  readonly pagination?: {
146
147
  readonly left?: readonly [{
@@ -356,6 +357,7 @@ export declare function schema(configure: (env: Environment) => void, _blockRef:
356
357
  }>>>;
357
358
  readonly: Schema.optional<Schema.Schema<string, string, never>>;
358
359
  }>>>;
360
+ rowKey: Schema.optional<Schema.Schema<string, string, never>>;
359
361
  cellStyle: Schema.optional<Schema.Schema<string, string, never>>;
360
362
  style: Schema.optional<typeof Schema.String>;
361
363
  pagination: Schema.optional<Schema.Struct<{
@@ -26,6 +26,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
26
26
  }>;
27
27
  actions?: import("../../../../table/schema.js").TableActionsValue;
28
28
  query?: import("../../../schema.js").FormConfigValue;
29
+ rowKey?: string;
29
30
  cellStyle?: string;
30
31
  style?: string;
31
32
  pagination?: import("effect/Schema").Schema.Type<typeof import("../../../../table/schema.js").Pagination>;
@@ -88,6 +89,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
88
89
  }>;
89
90
  actions?: import("../../../../table/schema.js").TableActionsValue;
90
91
  query?: import("../../../schema.js").FormConfigValue;
92
+ rowKey?: string;
91
93
  cellStyle?: string;
92
94
  style?: string;
93
95
  pagination?: import("effect/Schema").Schema.Type<typeof import("../../../../table/schema.js").Pagination>;
@@ -26,6 +26,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
26
26
  }>;
27
27
  actions?: import("../../../../table/schema.js").TableActionsValue;
28
28
  query?: import("../../../schema.js").FormConfigValue;
29
+ rowKey?: string;
29
30
  cellStyle?: string;
30
31
  style?: string;
31
32
  pagination?: import("effect/Schema").Schema.Type<typeof import("../../../../table/schema.js").Pagination>;
@@ -88,6 +89,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
88
89
  }>;
89
90
  actions?: import("../../../../table/schema.js").TableActionsValue;
90
91
  query?: import("../../../schema.js").FormConfigValue;
92
+ rowKey?: string;
91
93
  cellStyle?: string;
92
94
  style?: string;
93
95
  pagination?: import("effect/Schema").Schema.Type<typeof import("../../../../table/schema.js").Pagination>;
@@ -42,6 +42,7 @@ export declare function schema(configure: (env: Environment) => void): Schema.St
42
42
  dataSource?: import("../../../../table/schema.js").DataSourceValue;
43
43
  actions?: import("../../../../table/schema.js").TableActionsValue;
44
44
  query?: import("../../../schema.js").FormConfigValue;
45
+ rowKey?: string;
45
46
  cellStyle?: string;
46
47
  style?: string;
47
48
  pagination?: Schema.Schema.Type<typeof import("../../../../table/schema.js").Pagination>;
@@ -76,6 +77,7 @@ export declare function schema(configure: (env: Environment) => void): Schema.St
76
77
  dataSource?: import("../../../../table/schema.js").DataSourceValue;
77
78
  actions?: import("../../../../table/schema.js").TableActionsValue;
78
79
  query?: import("../../../schema.js").FormConfigValue;
80
+ rowKey?: string;
79
81
  cellStyle?: string;
80
82
  style?: string;
81
83
  pagination?: Schema.Schema.Type<typeof import("../../../../table/schema.js").Pagination>;
@@ -0,0 +1,10 @@
1
+ type __VLS_ModelProps = {
2
+ modelValue: Record<string, any>;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
+ "update:modelValue": (value: Record<string, any>) => any;
6
+ }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
7
+ "onUpdate:modelValue"?: ((value: Record<string, any>) => any) | undefined;
8
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ declare const _default: typeof __VLS_export;
10
+ export default _default;
@@ -0,0 +1,10 @@
1
+ <script setup>
2
+ defineOptions({ name: "ShwfedTableDragHandleRendererConfig" });
3
+ defineModel({ type: Object, ...{ required: true } });
4
+ </script>
5
+
6
+ <template>
7
+ <p class="text-xs text-zinc-500">
8
+ 本列无可配置项。
9
+ </p>
10
+ </template>
@@ -0,0 +1,10 @@
1
+ type __VLS_ModelProps = {
2
+ modelValue: Record<string, any>;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
+ "update:modelValue": (value: Record<string, any>) => any;
6
+ }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
7
+ "onUpdate:modelValue"?: ((value: Record<string, any>) => any) | undefined;
8
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ declare const _default: typeof __VLS_export;
10
+ export default _default;
@@ -0,0 +1,9 @@
1
+ import type { CellContext } from '@tanstack/vue-table';
2
+ import type { Value } from './schema.js';
3
+ type __VLS_Props = {
4
+ column: Value;
5
+ ctx: CellContext<unknown, unknown>;
6
+ };
7
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const _default: typeof __VLS_export;
9
+ export default _default;
@@ -0,0 +1,68 @@
1
+ <script setup>
2
+ import { computed, onBeforeUnmount, ref, watch } from "vue";
3
+ import { Icon } from "@iconify/vue";
4
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../../../../ui/tooltip";
5
+ import { injectReorderContext } from "../../../utils/reorder";
6
+ defineOptions({ name: "ShwfedTableDragHandleRendererRuntime" });
7
+ const props = defineProps({
8
+ column: { type: null, required: true },
9
+ ctx: { type: Object, required: true }
10
+ });
11
+ const reorder = injectReorderContext();
12
+ const enabled = computed(() => reorder?.enabled.value ?? false);
13
+ const reason = computed(() => reorder?.disabledReason.value ?? null);
14
+ const gripRef = ref(null);
15
+ watch(
16
+ [() => props.ctx.row.id, gripRef, enabled],
17
+ ([rowId, el, isEnabled], [prevRowId]) => {
18
+ if (!reorder) return;
19
+ if (prevRowId && prevRowId !== rowId) {
20
+ reorder.registerHandle(prevRowId, null);
21
+ }
22
+ reorder.registerHandle(rowId, isEnabled ? el : null);
23
+ },
24
+ { immediate: true, flush: "post" }
25
+ );
26
+ onBeforeUnmount(() => {
27
+ if (!reorder) return;
28
+ reorder.registerHandle(props.ctx.row.id, null);
29
+ });
30
+ </script>
31
+
32
+ <template>
33
+ <div class="flex items-center justify-center w-full h-full">
34
+ <Tooltip
35
+ v-if="!enabled && reason"
36
+ :delay-duration="400"
37
+ >
38
+ <TooltipTrigger as-child>
39
+ <span
40
+ class="inline-flex items-center justify-center size-6 text-zinc-300 cursor-not-allowed select-none"
41
+ aria-disabled="true"
42
+ >
43
+ <Icon
44
+ icon="fluent:re-order-dots-vertical-20-regular"
45
+ class="size-4"
46
+ />
47
+ </span>
48
+ </TooltipTrigger>
49
+ <TooltipContent>
50
+ {{ reason }}
51
+ </TooltipContent>
52
+ </Tooltip>
53
+ <span
54
+ v-else
55
+ ref="gripRef"
56
+ :class="[
57
+ 'inline-flex items-center justify-center size-6 select-none',
58
+ enabled ? 'text-zinc-400 hover:text-zinc-600 cursor-grab active:cursor-grabbing' : 'text-zinc-300 cursor-not-allowed'
59
+ ]"
60
+ :aria-disabled="!enabled || void 0"
61
+ >
62
+ <Icon
63
+ icon="fluent:re-order-dots-vertical-20-regular"
64
+ class="size-4"
65
+ />
66
+ </span>
67
+ </div>
68
+ </template>
@@ -0,0 +1,9 @@
1
+ import type { CellContext } from '@tanstack/vue-table';
2
+ import type { Value } from './schema.js';
3
+ type __VLS_Props = {
4
+ column: Value;
5
+ ctx: CellContext<unknown, unknown>;
6
+ };
7
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const _default: typeof __VLS_export;
9
+ export default _default;
@@ -0,0 +1,17 @@
1
+ import { Schema } from 'effect';
2
+ import type { ColumnDef } from '@tanstack/vue-table';
3
+ import type { Environment } from '../../../../../vendor/cel-js/lib/index.js';
4
+ export declare const type: "com.shwfed.table.column.drag-handle";
5
+ export declare const compatibilityDate: "2026-05-21";
6
+ export declare const metadata: {
7
+ readonly name: "拖拽";
8
+ readonly icon: "fluent:re-order-dots-vertical-20-regular";
9
+ };
10
+ export declare function schema(_configure: (env: Environment) => void): Schema.Struct<{
11
+ id: Schema.refine<string, typeof Schema.String>;
12
+ groupId: Schema.optional<typeof Schema.UUID>;
13
+ type: Schema.Literal<["com.shwfed.table.column.drag-handle"]>;
14
+ compatibilityDate: Schema.Literal<["2026-05-21"]>;
15
+ }>;
16
+ export type Value = Schema.Schema.Type<ReturnType<typeof schema>>;
17
+ export declare function toColumnDef(_value: Value): Partial<ColumnDef<unknown, unknown>>;
@@ -0,0 +1,31 @@
1
+ import { Schema } from "effect";
2
+ import { h } from "vue";
3
+ import { Icon } from "@iconify/vue";
4
+ import { columnIdentityFields } from "../../../utils/shared.js";
5
+ export const type = "com.shwfed.table.column.drag-handle";
6
+ export const compatibilityDate = "2026-05-21";
7
+ export const metadata = {
8
+ name: "\u62D6\u62FD",
9
+ icon: "fluent:re-order-dots-vertical-20-regular"
10
+ };
11
+ export function schema(_configure) {
12
+ return Schema.Struct({
13
+ type: Schema.Literal(type),
14
+ compatibilityDate: Schema.Literal(compatibilityDate),
15
+ ...columnIdentityFields()
16
+ }).annotations({ title: "DragHandleRenderer", description: "\u884C\u62D6\u62FD\u624B\u67C4\u5217" });
17
+ }
18
+ export function toColumnDef(_value) {
19
+ return {
20
+ header: () => h(Icon, {
21
+ "icon": "fluent:re-order-dots-vertical-20-regular",
22
+ "class": "size-3.5 text-zinc-400",
23
+ "aria-hidden": "true"
24
+ }),
25
+ size: 40,
26
+ enableSorting: false,
27
+ enableResizing: false,
28
+ enableHiding: false,
29
+ meta: { grow: false }
30
+ };
31
+ }
@@ -9,9 +9,9 @@ type __VLS_ModelProps = {
9
9
  modelValue: AnyRecord;
10
10
  };
11
11
  type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
12
- declare var __VLS_201: {};
12
+ declare var __VLS_232: {};
13
13
  type __VLS_Slots = {} & {
14
- 'general-extra'?: (props: typeof __VLS_201) => any;
14
+ 'general-extra'?: (props: typeof __VLS_232) => any;
15
15
  };
16
16
  declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
17
17
  "update:modelValue": (value: AnyRecord) => any;
@@ -38,6 +38,26 @@ import TableAiColumnsButton from "./ai/columns-button.vue";
38
38
  import TableAiDataSourceButton from "./ai/data-source-button.vue";
39
39
  import { Environment } from "../../vendor/cel-js/lib/index";
40
40
  defineOptions({ name: "ShwfedTableConfig" });
41
+ const RowKeyCELContext = defineComponent({
42
+ name: "RowKeyCELContext",
43
+ setup(_, { slots }) {
44
+ provideCELContext({
45
+ row: {
46
+ type: "dyn",
47
+ label: "\u5F53\u524D\u884C",
48
+ description: "\u5F53\u524D\u6E32\u67D3\u7684\u6574\u884C\u6570\u636E\uFF0C\u7C7B\u578B\u5728\u914D\u7F6E\u65F6\u672A\u77E5\uFF08`dyn`\uFF09\uFF0C\u5177\u4F53\u5B57\u6BB5\u53D6\u51B3\u4E8E\u63A5\u5165\u6570\u636E\u6E90\u3002",
49
+ value: void 0
50
+ },
51
+ index: {
52
+ type: "number",
53
+ label: "\u884C\u5E8F\u53F7",
54
+ description: "\u5F53\u524D\u884C\u5728\u6240\u6709\u6570\u636E\u4E2D\u7684\u5E8F\u53F7\uFF0C\u4ECE `0` \u5F00\u59CB\u3002",
55
+ value: 0
56
+ }
57
+ });
58
+ return () => slots.default?.();
59
+ }
60
+ });
41
61
  const CellStyleCELContext = defineComponent({
42
62
  name: "CellStyleCELContext",
43
63
  setup(_, { slots }) {
@@ -444,13 +464,12 @@ function groupLabel(group) {
444
464
  }
445
465
  function columnLabel(col) {
446
466
  if (!col) return "(\u672A\u77E5)";
447
- if (col.type === "com.shwfed.table.column.select") return "\u9009\u62E9\u5217";
448
- if (!col.title) return "(\u65E0\u6807\u9898)";
449
467
  if (Array.isArray(col.title)) {
450
468
  const zh = col.title.find((t) => t.locale === "zh");
451
- return zh?.message || col.title[0]?.message || "(\u65E0\u6807\u9898)";
469
+ const msg = zh?.message || col.title[0]?.message;
470
+ if (msg) return msg;
452
471
  }
453
- return "(\u65E0\u6807\u9898)";
472
+ return getColumnTypeLabel(col.type, col.compatibilityDate);
454
473
  }
455
474
  function getColumnTypeLabel(type, compatibilityDate) {
456
475
  return findColumnEntry(type, compatibilityDate)?.metadata.name ?? type;
@@ -1265,6 +1284,36 @@ const tableQueryValue = computed({
1265
1284
  </InputGroup>
1266
1285
  </Field>
1267
1286
 
1287
+ <Field orientation="vertical">
1288
+ <FieldLabel class="text-xs text-zinc-500">
1289
+ <template
1290
+ v-if="generalFieldDescription('rowKey')"
1291
+ #tooltip
1292
+ >
1293
+ <Markdown
1294
+ :source="generalFieldDescription('rowKey')"
1295
+ block
1296
+ class="prose prose-sm prose-zinc"
1297
+ />
1298
+ </template>
1299
+ {{ generalFieldTitle("rowKey") }}
1300
+ </FieldLabel>
1301
+ <RowKeyCELContext>
1302
+ <ExpressionEditor
1303
+ :model-value="editingGeneralConfig.rowKey ?? ''"
1304
+ placeholder="如 row.id"
1305
+ result-type="string"
1306
+ @update:model-value="(v) => updateGeneralOptionalString('rowKey', v)"
1307
+ />
1308
+ </RowKeyCELContext>
1309
+ <p
1310
+ v-if="getError('rowKey')"
1311
+ class="text-xs text-red-500"
1312
+ >
1313
+ {{ getError("rowKey") }}
1314
+ </p>
1315
+ </Field>
1316
+
1268
1317
  <!-- Host extension slot: a wrapping editor (e.g. the `table` form
1269
1318
  field config) injects its own fields into the general pane. -->
1270
1319
  <slot name="general-extra" />
@@ -9,9 +9,9 @@ type __VLS_ModelProps = {
9
9
  modelValue: AnyRecord;
10
10
  };
11
11
  type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
12
- declare var __VLS_201: {};
12
+ declare var __VLS_232: {};
13
13
  type __VLS_Slots = {} & {
14
- 'general-extra'?: (props: typeof __VLS_201) => any;
14
+ 'general-extra'?: (props: typeof __VLS_232) => any;
15
15
  };
16
16
  declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
17
17
  "update:modelValue": (value: AnyRecord) => any;
@@ -27,6 +27,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
27
27
  }>;
28
28
  actions?: import("./schema.js").TableActionsValue;
29
29
  query?: import("../form/schema.js").FormConfigValue;
30
+ rowKey?: string;
30
31
  cellStyle?: string;
31
32
  style?: string;
32
33
  pagination?: import("effect/Schema").Schema.Type<typeof import("./schema.js").Pagination>;
@@ -71,6 +72,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
71
72
  }>;
72
73
  actions?: import("./schema.js").TableActionsValue;
73
74
  query?: import("../form/schema.js").FormConfigValue;
75
+ rowKey?: string;
74
76
  cellStyle?: string;
75
77
  style?: string;
76
78
  pagination?: import("effect/Schema").Schema.Type<typeof import("./schema.js").Pagination>;
@@ -30,6 +30,10 @@ import { provideTableInstanceId } from "./utils/instance";
30
30
  import { provideEventTarget } from "../../share/event-bus";
31
31
  import { findColumn } from "./utils/resolve";
32
32
  import { interpolateMarkdown } from "./utils/runtime";
33
+ import { carrySymbolId, createRowKeyResolver } from "./utils/row-key";
34
+ import { provideReorderContext } from "./utils/reorder";
35
+ import { applyReorder, useRowReorder } from "./utils/row-reorder";
36
+ import { type as DRAG_HANDLE_COLUMN_TYPE } from "./columns/2026-05-21/com.shwfed.table.column.drag-handle/schema";
33
37
  defineOptions({ name: "ShwfedTable" });
34
38
  const config = defineModel("config", { type: Object });
35
39
  const rowData = defineModel("rows", { type: Array, ...{ default: () => [] } });
@@ -46,7 +50,12 @@ const { t, locale } = useI18n({
46
50
  "sort-asc": "\u5347\u5E8F",
47
51
  "sort-desc": "\u964D\u5E8F",
48
52
  "sort-unsorted": "\u672A\u6392\u5E8F",
49
- "column-tooltip": "\u67E5\u770B\u5217\u63D0\u793A\uFF1A{column}"
53
+ "column-tooltip": "\u67E5\u770B\u5217\u63D0\u793A\uFF1A{column}",
54
+ "reorder-disabled-sort": "\u5DF2\u6392\u5E8F\u65F6\u4E0D\u53EF\u62D6\u62FD\uFF0C\u8BF7\u5148\u53D6\u6D88\u6392\u5E8F",
55
+ "reorder-disabled-filter": "\u5DF2\u7B5B\u9009\u65F6\u4E0D\u53EF\u62D6\u62FD\uFF0C\u8BF7\u5148\u6E05\u9664\u7B5B\u9009",
56
+ "reorder-disabled-grouping": "\u5DF2\u5206\u7EC4\u65F6\u4E0D\u53EF\u62D6\u62FD\uFF0C\u8BF7\u5148\u53D6\u6D88\u5206\u7EC4",
57
+ "reorder-disabled-pinning": "\u5DF2\u56FA\u5B9A\u884C\u65F6\u4E0D\u53EF\u62D6\u62FD\uFF0C\u8BF7\u5148\u53D6\u6D88\u56FA\u5B9A",
58
+ "reorder-disabled-manual-pagination": "\u670D\u52A1\u7AEF\u5206\u9875\u4E0B\u4E0D\u53EF\u672C\u5730\u62D6\u62FD\u6392\u5E8F"
50
59
  },
51
60
  en: {
52
61
  "total": "Total {count} items",
@@ -55,7 +64,12 @@ const { t, locale } = useI18n({
55
64
  "sort-asc": "Ascending",
56
65
  "sort-desc": "Descending",
57
66
  "sort-unsorted": "Unsorted",
58
- "column-tooltip": "Show column tooltip: {column}"
67
+ "column-tooltip": "Show column tooltip: {column}",
68
+ "reorder-disabled-sort": "Drag disabled while sorted \u2014 clear sorting first",
69
+ "reorder-disabled-filter": "Drag disabled while filtered \u2014 clear filters first",
70
+ "reorder-disabled-grouping": "Drag disabled while grouped \u2014 clear grouping first",
71
+ "reorder-disabled-pinning": "Drag disabled while rows are pinned \u2014 unpin first",
72
+ "reorder-disabled-manual-pagination": "Drag disabled with server-side pagination"
59
73
  },
60
74
  ja: {
61
75
  "total": "\u5408\u8A08 {count} \u4EF6",
@@ -64,7 +78,12 @@ const { t, locale } = useI18n({
64
78
  "sort-asc": "\u6607\u9806",
65
79
  "sort-desc": "\u964D\u9806",
66
80
  "sort-unsorted": "\u672A\u30BD\u30FC\u30C8",
67
- "column-tooltip": "\u5217\u30C4\u30FC\u30EB\u30C1\u30C3\u30D7\u3092\u8868\u793A: {column}"
81
+ "column-tooltip": "\u5217\u30C4\u30FC\u30EB\u30C1\u30C3\u30D7\u3092\u8868\u793A: {column}",
82
+ "reorder-disabled-sort": "\u30BD\u30FC\u30C8\u4E2D\u306F\u30C9\u30E9\u30C3\u30B0\u3067\u304D\u307E\u305B\u3093\u3002\u30BD\u30FC\u30C8\u3092\u89E3\u9664\u3057\u3066\u304F\u3060\u3055\u3044",
83
+ "reorder-disabled-filter": "\u30D5\u30A3\u30EB\u30BF\u30FC\u4E2D\u306F\u30C9\u30E9\u30C3\u30B0\u3067\u304D\u307E\u305B\u3093\u3002\u30D5\u30A3\u30EB\u30BF\u30FC\u3092\u89E3\u9664\u3057\u3066\u304F\u3060\u3055\u3044",
84
+ "reorder-disabled-grouping": "\u30B0\u30EB\u30FC\u30D7\u5316\u4E2D\u306F\u30C9\u30E9\u30C3\u30B0\u3067\u304D\u307E\u305B\u3093\u3002\u30B0\u30EB\u30FC\u30D7\u5316\u3092\u89E3\u9664\u3057\u3066\u304F\u3060\u3055\u3044",
85
+ "reorder-disabled-pinning": "\u56FA\u5B9A\u884C\u304C\u3042\u308B\u305F\u3081\u30C9\u30E9\u30C3\u30B0\u3067\u304D\u307E\u305B\u3093\u3002\u56FA\u5B9A\u3092\u89E3\u9664\u3057\u3066\u304F\u3060\u3055\u3044",
86
+ "reorder-disabled-manual-pagination": "\u30B5\u30FC\u30D0\u30FC\u30B5\u30A4\u30C9\u30DA\u30FC\u30B8\u30F3\u30B0\u3067\u306F\u30C9\u30E9\u30C3\u30B0\u4E26\u3079\u66FF\u3048\u306F\u3067\u304D\u307E\u305B\u3093"
68
87
  }
69
88
  }
70
89
  });
@@ -171,11 +190,27 @@ function getRowAt(i) {
171
190
  function setRowAt(i, next) {
172
191
  const rows2 = rowData.value;
173
192
  if (i < 0 || i >= rows2.length) return;
193
+ carrySymbolId(rows2[i], next);
174
194
  rowData.value = rows2.map((r, j) => j === i ? next : r);
175
195
  }
176
196
  const derivedEditableColumns = computed(
177
197
  () => (config.value?.columns ?? []).filter((c) => typeof c.binding === "string" && !!c.derived)
178
198
  );
199
+ const getRowId = createRowKeyResolver({
200
+ evaluateKey: (row, index) => {
201
+ const expr = config.value?.rowKey;
202
+ if (!expr) return void 0;
203
+ try {
204
+ const result = Effect.runSync($cel(expr, mergeCelContext({ row, index })));
205
+ if (typeof result === "string") return result;
206
+ if (typeof result === "number" && Number.isFinite(result)) return result;
207
+ return void 0;
208
+ } catch (e) {
209
+ console.error("[shwfed-table] rowKey expression failed", e);
210
+ return void 0;
211
+ }
212
+ }
213
+ });
179
214
  const tableApi = useVueTable({
180
215
  get columns() {
181
216
  return columns.value;
@@ -197,6 +232,7 @@ const tableApi = useVueTable({
197
232
  }
198
233
  };
199
234
  },
235
+ getRowId,
200
236
  getCoreRowModel: getCoreRowModel(),
201
237
  getExpandedRowModel: getExpandedRowModel(),
202
238
  getPaginationRowModel: getPaginationRowModel(),
@@ -221,6 +257,55 @@ provideCELContext({
221
257
  }
222
258
  }
223
259
  });
260
+ const hasDragHandleColumn = computed(() => (config.value?.columns ?? []).some((c) => c.type === DRAG_HANDLE_COLUMN_TYPE));
261
+ const reorderDisabledReason = computed(() => {
262
+ if (!hasDragHandleColumn.value) return null;
263
+ const state = tableApi.getState();
264
+ if (state.sorting.length > 0) return t("reorder-disabled-sort");
265
+ if (state.columnFilters.length > 0) return t("reorder-disabled-filter");
266
+ const gf = state.globalFilter;
267
+ if (typeof gf === "string" && gf.length > 0) return t("reorder-disabled-filter");
268
+ if (state.grouping.length > 0) return t("reorder-disabled-grouping");
269
+ const rp = state.rowPinning;
270
+ if ((rp?.top?.length ?? 0) > 0 || (rp?.bottom?.length ?? 0) > 0) {
271
+ return t("reorder-disabled-pinning");
272
+ }
273
+ if (isManualPagination.value) return t("reorder-disabled-manual-pagination");
274
+ return null;
275
+ });
276
+ const isReorderable = computed(() => hasDragHandleColumn.value && reorderDisabledReason.value === null);
277
+ const rowReorder = useRowReorder({
278
+ isEnabled: () => isReorderable.value,
279
+ onReorder: (sourceId, targetId, position) => reorderRow(sourceId, targetId, position)
280
+ });
281
+ function reorderRow(sourceId, targetId, position) {
282
+ if (sourceId === targetId) return;
283
+ const flat = tableApi.getCoreRowModel().rows;
284
+ const src = flat.find((r) => r.id === sourceId);
285
+ const tgt = flat.find((r) => r.id === targetId);
286
+ if (!src || !tgt) return;
287
+ const next = applyReorder(rowData.value, src.index, tgt.index, position);
288
+ if (next === rowData.value) return;
289
+ rowData.value = next;
290
+ }
291
+ provideReorderContext({
292
+ enabled: isReorderable,
293
+ disabledReason: reorderDisabledReason,
294
+ registerHandle: rowReorder.setHandle,
295
+ instructionFor: rowReorder.instructionFor
296
+ });
297
+ const rowRefCache = /* @__PURE__ */ new Map();
298
+ function getRowRefFn(rowId) {
299
+ let fn = rowRefCache.get(rowId);
300
+ if (!fn) {
301
+ fn = (el) => {
302
+ measureRow(el);
303
+ rowReorder.setRow(rowId, el instanceof HTMLElement ? el : null);
304
+ };
305
+ rowRefCache.set(rowId, fn);
306
+ }
307
+ return fn;
308
+ }
224
309
  async function fetchDataSource() {
225
310
  const dataSource = config.value?.dataSource;
226
311
  if (!dataSource) return;
@@ -704,11 +789,32 @@ export { TableConfig, createTableConfig, getColumnTechnicalKey } from "./schema"
704
789
  :set-row="setRowAt"
705
790
  >
706
791
  <tr
707
- :ref="measureRow"
792
+ :ref="getRowRefFn(rows[r.index].id)"
708
793
  class="flex absolute w-full border-b border-zinc-300"
709
794
  :data-index="rows[r.index].index"
710
795
  :style="{ transform: `translate3d(0, ${r.start}px, 0)` }"
711
796
  >
797
+ <!--
798
+ Drop indicator. Absolutely positioned so it overlays
799
+ the row without affecting flex layout, and sits at
800
+ z-30 above sticky pinned cells (which use z-15) so
801
+ the line is never hidden behind a column. The bar is
802
+ offset by -1px so its 2px height is centered exactly
803
+ on the row boundary — for adjacent rows, "reorder-
804
+ below on i" and "reorder-above on i+1" then resolve
805
+ to the same pixel band, so the indicator doesn't
806
+ jitter as the hitbox flips between them.
807
+ -->
808
+ <span
809
+ v-if="rowReorder.instructionFor(rows[r.index].id) === 'reorder-above'"
810
+ aria-hidden="true"
811
+ class="absolute left-0 right-0 -top-px h-0.5 z-30 pointer-events-none bg-(--primary)"
812
+ />
813
+ <span
814
+ v-else-if="rowReorder.instructionFor(rows[r.index].id) === 'reorder-below'"
815
+ aria-hidden="true"
816
+ class="absolute left-0 right-0 -bottom-px h-0.5 z-30 pointer-events-none bg-(--primary)"
817
+ />
712
818
  <td
713
819
  v-for="cell in rows[r.index].getVisibleCells()"
714
820
  :key="cell.id"
@@ -27,6 +27,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
27
27
  }>;
28
28
  actions?: import("./schema.js").TableActionsValue;
29
29
  query?: import("../form/schema.js").FormConfigValue;
30
+ rowKey?: string;
30
31
  cellStyle?: string;
31
32
  style?: string;
32
33
  pagination?: import("effect/Schema").Schema.Type<typeof import("./schema.js").Pagination>;
@@ -71,6 +72,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
71
72
  }>;
72
73
  actions?: import("./schema.js").TableActionsValue;
73
74
  query?: import("../form/schema.js").FormConfigValue;
75
+ rowKey?: string;
74
76
  cellStyle?: string;
75
77
  style?: string;
76
78
  pagination?: import("effect/Schema").Schema.Type<typeof import("./schema.js").Pagination>;
@@ -220,6 +220,7 @@ export declare function TableConfig(configure: (env: Environment) => void): Sche
220
220
  }[];
221
221
  readonly readonly?: string | undefined;
222
222
  } | undefined;
223
+ readonly rowKey?: string | undefined;
223
224
  readonly cellStyle?: string | undefined;
224
225
  readonly pagination?: {
225
226
  readonly left?: readonly [{
@@ -435,6 +436,7 @@ export declare function TableConfig(configure: (env: Environment) => void): Sche
435
436
  }>>>;
436
437
  readonly: Schema.optional<Schema.Schema<string, string, never>>;
437
438
  }>>>;
439
+ rowKey: Schema.optional<Schema.Schema<string, string, never>>;
438
440
  cellStyle: Schema.optional<Schema.Schema<string, string, never>>;
439
441
  style: Schema.optional<typeof Schema.String>;
440
442
  pagination: Schema.optional<Schema.Struct<{
@@ -598,6 +600,7 @@ export declare function createTableConfig(body: Omit<Schema.Schema.Type<ReturnTy
598
600
  }[];
599
601
  readonly readonly?: string | undefined;
600
602
  } | undefined;
603
+ rowKey?: string | undefined;
601
604
  cellStyle?: string | undefined;
602
605
  pagination?: {
603
606
  readonly left?: readonly [{
@@ -672,6 +675,7 @@ export type TableConfigValue = Readonly<{
672
675
  dataSource?: DataSourceValue;
673
676
  actions?: TableActionsValue;
674
677
  query?: FormConfigValue;
678
+ rowKey?: string;
675
679
  cellStyle?: string;
676
680
  style?: string;
677
681
  pagination?: Schema.Schema.Type<typeof Pagination>;
@@ -4,7 +4,7 @@ import { Locale } from "../../share/locale.js";
4
4
  import { ActionSchemaFields } from "../actions/schema.js";
5
5
  import { FormConfig } from "../form/schema.js";
6
6
  import { allColumnSchemas } from "./utils/resolve.js";
7
- import { registerRowVariablesIfAbsent } from "./utils/shared.js";
7
+ import { CelRowAccess, registerRowVariablesIfAbsent } from "./utils/shared.js";
8
8
  export { getStructFieldTitle, getStructFieldDescription } from "./utils/schema-meta.js";
9
9
  const KIND = "shwfed.component.table";
10
10
  export const metadata = {
@@ -195,6 +195,9 @@ export function TableConfig(configure) {
195
195
  },
196
196
  resultType: (type) => type.startsWith("map")
197
197
  });
198
+ const CelRowKey = CelRowAccess(configure, {
199
+ resultType: (type) => type === "string" || type === "number" || type === "dyn"
200
+ });
198
201
  const dataSourceSchema = DataSource(configure);
199
202
  const { fields: actionFields } = ActionSchemaFields((env) => {
200
203
  configure(env);
@@ -238,6 +241,10 @@ export function TableConfig(configure) {
238
241
  title: "\u641C\u7D22\u6761\u4EF6",
239
242
  description: "\u6E32\u67D3\u5728\u8868\u683C\u4E0A\u65B9\u7684\u641C\u7D22\u6761\u4EF6\u8868\u5355\uFF1B\u5176\u5F53\u524D\u503C\u4EE5 `query` \u53D8\u91CF\u6CE8\u5165\u6570\u636E\u6E90\u8868\u8FBE\u5F0F"
240
243
  }),
244
+ rowKey: Schema.optional(CelRowKey).annotations({
245
+ title: "\u884C\u552F\u4E00\u6807\u8BC6",
246
+ description: "\u4E3A\u6BCF\u4E00\u884C\u8BA1\u7B97\u7A33\u5B9A\u552F\u4E00 ID \u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u53EF\u8BBF\u95EE `row` / `index`\uFF0C\u7ED3\u679C\u987B\u4E3A\u5B57\u7B26\u4E32\u6216\u6570\u5B57\uFF08\u4F8B\u5982 `row.id`\uFF09\u3002\u914D\u7F6E\u540E\uFF0C\u8DE8\u5237\u65B0\u7684\u52FE\u9009\u3001\u5C55\u5F00\u7B49\u884C\u7EA7\u72B6\u6001\u6309\u8BE5 ID \u5339\u914D\uFF1B\u7559\u7A7A\u65F6\u884C\u8EAB\u4EFD\u6309\u5BF9\u8C61\u5F15\u7528\u515C\u5E95\uFF0C\u8FDC\u7AEF\u6570\u636E\u5237\u65B0\u4F1A\u4E22"
247
+ }),
241
248
  cellStyle: Schema.optional(CelCellStyle).annotations({
242
249
  title: "\u5355\u5143\u683C\u6837\u5F0F",
243
250
  description: "\u5355\u5143\u683C\u6837\u5F0F CEL \u8868\u8FBE\u5F0F"
@@ -0,0 +1,10 @@
1
+ import type { Ref } from 'vue';
2
+ import type { RowReorderInstruction } from './row-reorder.js';
3
+ export interface ReorderContext {
4
+ enabled: Ref<boolean>;
5
+ disabledReason: Ref<string | null>;
6
+ registerHandle: (rowId: string, el: HTMLElement | null) => void;
7
+ instructionFor: (rowId: string) => RowReorderInstruction | null;
8
+ }
9
+ export declare function provideReorderContext(ctx: ReorderContext): void;
10
+ export declare function injectReorderContext(): ReorderContext | null;
@@ -0,0 +1,8 @@
1
+ import { inject, provide } from "vue";
2
+ const KEY = Symbol("shwfed-table-reorder");
3
+ export function provideReorderContext(ctx) {
4
+ provide(KEY, ctx);
5
+ }
6
+ export function injectReorderContext() {
7
+ return inject(KEY, null);
8
+ }
@@ -0,0 +1,11 @@
1
+ export declare const ROW_ID_SYMBOL: unique symbol;
2
+ export declare function readSymbolId(row: object): string | undefined;
3
+ export declare function stampSymbolId(row: object, id?: string): string;
4
+ export declare function carrySymbolId(prev: unknown, next: unknown): void;
5
+ export interface RowKeyResolver {
6
+ (row: unknown, index: number): string;
7
+ }
8
+ export interface CreateRowKeyResolverOptions {
9
+ evaluateKey: (row: object, index: number) => string | number | undefined;
10
+ }
11
+ export declare function createRowKeyResolver(options: CreateRowKeyResolverOptions): RowKeyResolver;
@@ -0,0 +1,30 @@
1
+ import { toRaw } from "vue";
2
+ export const ROW_ID_SYMBOL = Symbol.for("shwfed.table.row-id");
3
+ export function readSymbolId(row) {
4
+ const id = toRaw(row)[ROW_ID_SYMBOL];
5
+ return typeof id === "string" ? id : void 0;
6
+ }
7
+ export function stampSymbolId(row, id) {
8
+ const next = id ?? crypto.randomUUID();
9
+ toRaw(row)[ROW_ID_SYMBOL] = next;
10
+ return next;
11
+ }
12
+ export function carrySymbolId(prev, next) {
13
+ if (!prev || typeof prev !== "object") return;
14
+ if (!next || typeof next !== "object") return;
15
+ const prevId = readSymbolId(prev);
16
+ if (!prevId) return;
17
+ if (readSymbolId(next) !== void 0) return;
18
+ stampSymbolId(next, prevId);
19
+ }
20
+ export function createRowKeyResolver(options) {
21
+ return (row, index) => {
22
+ if (row === null || typeof row !== "object") return `__primitive_${index}`;
23
+ const obj = row;
24
+ const keyed = options.evaluateKey(obj, index);
25
+ if (keyed !== void 0 && keyed !== null && keyed !== "") {
26
+ return `k:${String(keyed)}`;
27
+ }
28
+ return readSymbolId(obj) ?? stampSymbolId(obj);
29
+ };
30
+ }
@@ -0,0 +1,12 @@
1
+ export type RowReorderInstruction = 'reorder-above' | 'reorder-below';
2
+ export declare function applyReorder<T>(rows: ReadonlyArray<T>, sourceIndex: number, targetIndex: number, position: RowReorderInstruction): ReadonlyArray<T>;
3
+ export interface UseRowReorderOptions {
4
+ onReorder: (sourceId: string, targetId: string, position: RowReorderInstruction) => void;
5
+ isEnabled: () => boolean;
6
+ }
7
+ export interface RowReorderApi {
8
+ setRow: (rowId: string, el: HTMLElement | null) => void;
9
+ setHandle: (rowId: string, el: HTMLElement | null) => void;
10
+ instructionFor: (rowId: string) => RowReorderInstruction | null;
11
+ }
12
+ export declare function useRowReorder(options: UseRowReorderOptions): RowReorderApi;
@@ -0,0 +1,162 @@
1
+ import { onBeforeUnmount, ref } from "vue";
2
+ import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
3
+ import {
4
+ draggable,
5
+ dropTargetForElements
6
+ } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
7
+ import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview";
8
+ import { preserveOffsetOnSource } from "@atlaskit/pragmatic-drag-and-drop/element/preserve-offset-on-source";
9
+ import {
10
+ attachInstruction,
11
+ extractInstruction
12
+ } from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item";
13
+ export function applyReorder(rows, sourceIndex, targetIndex, position) {
14
+ if (sourceIndex === targetIndex) return rows;
15
+ if (sourceIndex < 0 || sourceIndex >= rows.length) return rows;
16
+ if (targetIndex < 0 || targetIndex >= rows.length) return rows;
17
+ let insertIdx = targetIndex + (position === "reorder-below" ? 1 : 0);
18
+ if (sourceIndex < targetIndex) insertIdx -= 1;
19
+ if (insertIdx === sourceIndex) return rows;
20
+ const next = rows.slice();
21
+ const [moved] = next.splice(sourceIndex, 1);
22
+ next.splice(insertIdx, 0, moved);
23
+ return next;
24
+ }
25
+ const SOURCE_KIND = "shwfed-table-row";
26
+ export function useRowReorder(options) {
27
+ const instruction = ref(null);
28
+ const regs = /* @__PURE__ */ new Map();
29
+ function rewire(rowId, reg) {
30
+ reg.cleanup?.();
31
+ if (!reg.rowEl || !reg.handleEl) {
32
+ reg.cleanup = null;
33
+ return;
34
+ }
35
+ const rowEl = reg.rowEl;
36
+ const handleEl = reg.handleEl;
37
+ reg.cleanup = combine(
38
+ draggable({
39
+ element: rowEl,
40
+ dragHandle: handleEl,
41
+ getInitialData: () => ({ kind: SOURCE_KIND, id: rowId }),
42
+ canDrag: () => options.isEnabled(),
43
+ // The row is full-table-width and most of its content trails off-
44
+ // screen on wide tables, so the default native preview (a snapshot
45
+ // of `element`) is a giant useless rectangle. We render a clone
46
+ // capped at ~360px and fade it out to the right via a CSS mask so
47
+ // the user sees just enough of the row's leading cells to identify
48
+ // it, with the trailing content gracefully dissolving.
49
+ onGenerateDragPreview: ({ nativeSetDragImage, location, source }) => {
50
+ setCustomNativeDragPreview({
51
+ nativeSetDragImage,
52
+ getOffset: preserveOffsetOnSource({
53
+ element: source.element,
54
+ input: location.current.input
55
+ }),
56
+ render: ({ container }) => {
57
+ const src = source.element;
58
+ const rect = src.getBoundingClientRect();
59
+ const clone = src.cloneNode(true);
60
+ clone.style.transform = "none";
61
+ clone.style.position = "relative";
62
+ clone.style.top = "0";
63
+ clone.style.left = "0";
64
+ clone.style.height = `${rect.height}px`;
65
+ clone.style.width = `${rect.width}px`;
66
+ clone.style.flexShrink = "0";
67
+ const wrapper = document.createElement("div");
68
+ wrapper.style.width = `${Math.min(rect.width, 360)}px`;
69
+ wrapper.style.maxWidth = "60vw";
70
+ wrapper.style.height = `${rect.height}px`;
71
+ wrapper.style.overflow = "hidden";
72
+ wrapper.style.background = "white";
73
+ wrapper.style.boxShadow = "0 6px 18px rgba(0,0,0,0.12)";
74
+ wrapper.style.borderRadius = "4px";
75
+ const fade = "linear-gradient(to right, rgba(0,0,0,1) 0%, rgba(0,0,0,1) 55%, rgba(0,0,0,0) 100%)";
76
+ wrapper.style.maskImage = fade;
77
+ wrapper.style.webkitMaskImage = fade;
78
+ wrapper.appendChild(clone);
79
+ container.appendChild(wrapper);
80
+ }
81
+ });
82
+ }
83
+ }),
84
+ dropTargetForElements({
85
+ element: reg.rowEl,
86
+ canDrop: ({ source }) => {
87
+ if (!options.isEnabled()) return false;
88
+ return source.data.kind === SOURCE_KIND && source.data.id !== rowId;
89
+ },
90
+ getIsSticky: () => true,
91
+ getData: ({ input, element }) => attachInstruction(
92
+ { kind: `${SOURCE_KIND}-target`, id: rowId },
93
+ {
94
+ input,
95
+ element,
96
+ currentLevel: 0,
97
+ indentPerLevel: 0,
98
+ mode: "standard",
99
+ block: ["make-child", "reparent"]
100
+ }
101
+ ),
102
+ onDrag: ({ self }) => {
103
+ const inst = extractInstruction(self.data);
104
+ if (!inst || inst.type === "instruction-blocked") {
105
+ if (instruction.value?.id === rowId) instruction.value = null;
106
+ return;
107
+ }
108
+ if (inst.type === "reorder-above" || inst.type === "reorder-below") {
109
+ instruction.value = { id: rowId, type: inst.type };
110
+ }
111
+ },
112
+ onDragLeave: () => {
113
+ if (instruction.value?.id === rowId) instruction.value = null;
114
+ },
115
+ onDrop: ({ source, self }) => {
116
+ instruction.value = null;
117
+ const inst = extractInstruction(self.data);
118
+ if (!inst) return;
119
+ if (inst.type !== "reorder-above" && inst.type !== "reorder-below") return;
120
+ const sourceId = typeof source.data.id === "string" ? source.data.id : null;
121
+ if (!sourceId || sourceId === rowId) return;
122
+ options.onReorder(sourceId, rowId, inst.type);
123
+ }
124
+ })
125
+ );
126
+ }
127
+ function getReg(rowId) {
128
+ let reg = regs.get(rowId);
129
+ if (!reg) {
130
+ reg = { rowEl: null, handleEl: null, cleanup: null };
131
+ regs.set(rowId, reg);
132
+ }
133
+ return reg;
134
+ }
135
+ function purgeIfEmpty(rowId, reg) {
136
+ if (!reg.rowEl && !reg.handleEl && !reg.cleanup) regs.delete(rowId);
137
+ if (instruction.value?.id === rowId && !reg.rowEl) instruction.value = null;
138
+ }
139
+ function setRow(rowId, el) {
140
+ const reg = getReg(rowId);
141
+ if (reg.rowEl === el) return;
142
+ reg.rowEl = el;
143
+ rewire(rowId, reg);
144
+ purgeIfEmpty(rowId, reg);
145
+ }
146
+ function setHandle(rowId, el) {
147
+ const reg = getReg(rowId);
148
+ if (reg.handleEl === el) return;
149
+ reg.handleEl = el;
150
+ rewire(rowId, reg);
151
+ purgeIfEmpty(rowId, reg);
152
+ }
153
+ function instructionFor(rowId) {
154
+ return instruction.value?.id === rowId ? instruction.value.type : null;
155
+ }
156
+ onBeforeUnmount(() => {
157
+ for (const reg of regs.values()) reg.cleanup?.();
158
+ regs.clear();
159
+ instruction.value = null;
160
+ });
161
+ return { setRow, setHandle, instructionFor };
162
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shwfed/config",
3
- "version": "2.3.1",
3
+ "version": "2.3.3",
4
4
  "description": "Configurable UI for SHWFED",
5
5
  "type": "module",
6
6
  "publishConfig": {