@hybridly/vue 0.10.0-beta.26 → 0.10.0-beta.27

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.
package/dist/index.d.mts CHANGED
@@ -34,10 +34,8 @@ declare const Deferred: vue.DefineComponent<vue.ExtractPropTypes<{
34
34
  type Errors$1<T extends SearchableObject$1> = { [K in keyof T]?: T[K] extends Record<string, any> ? Errors$1<T[K]> : string };
35
35
  type FormFieldBehavior$1 = boolean | string[];
36
36
  type DefaultFormOptions = Pick<FormOptions<object>, 'timeout' | 'resetOnSuccess' | 'resetOnError' | 'setDefaultOnSuccess' | 'progress' | 'preserveScroll' | 'preserveState' | 'preserveUrl' | 'headers' | 'errorBag' | 'spoof' | 'transformUrl' | 'updateHistoryState' | 'useFormData'>;
37
- interface FormOptions<T extends SearchableObject$1> extends Omit<HybridRequestOptions, 'data' | 'url' | 'reset'> {
38
- fields: T;
37
+ type FormSubmitOptions$1<T extends SearchableObject$1> = Omit<HybridRequestOptions, 'data' | 'url' | 'reset'> & {
39
38
  url?: UrlResolvable | (() => UrlResolvable);
40
- key?: string | false;
41
39
  /**
42
40
  * Defines the delay after which the `recentlySuccessful` and `recentlyFailed` variables are reset to `false`.
43
41
  */
@@ -61,6 +59,27 @@ interface FormOptions<T extends SearchableObject$1> extends Omit<HybridRequestOp
61
59
  * Callback executed before the form submission for transforming the fields.
62
60
  */
63
61
  transform?: (fields: T) => any;
62
+ };
63
+ type FormAutomaticallySubmitOptions<T extends SearchableObject$1> = true | (FormSubmitOptions$1<T> & {
64
+ /**
65
+ * Debounce delay in milliseconds before submitting automatically.
66
+ * @default 100
67
+ */
68
+ debounce?: number;
69
+ /**
70
+ * Whether to immediately submit the form on any change. If `false`, submits after the `debounce` delay.
71
+ * @default true
72
+ */
73
+ immediate?: boolean;
74
+ });
75
+ interface FormOptions<T extends SearchableObject$1> extends FormSubmitOptions$1<T> {
76
+ fields: T;
77
+ key?: string | false;
78
+ /**
79
+ * Automatically submits the form when fields change.
80
+ * Pass `true` to use defaults or an object to customize submit overrides and debounce delay.
81
+ */
82
+ automaticallySubmit?: FormAutomaticallySubmitOptions<T>;
64
83
  }
65
84
  interface FormReturn<T extends SearchableObject$1, P extends Path$1<T> & string = Path$1<T> & string> {
66
85
  resetFields: (...keys: P[]) => void;
@@ -74,7 +93,7 @@ interface FormReturn<T extends SearchableObject$1, P extends Path$1<T> & string
74
93
  clearError: (key: P) => void;
75
94
  setDefault: (newDefault: Partial<T>) => void;
76
95
  hasDirty: (...keys: P[]) => boolean;
77
- submit: (optionsOverrides?: Omit<FormOptions<T>, 'fields' | 'key'>) => Promise<any>;
96
+ submit: (optionsOverrides?: FormSubmitOptions$1<T>) => Promise<any>;
78
97
  hasErrors: boolean;
79
98
  defaults: DeepReadonly<T>;
80
99
  loaded: DeepReadonly<T>;
@@ -292,7 +311,7 @@ declare const WhenVisible: vue.DefineComponent<vue.ExtractPropTypes<{
292
311
  }>> & Readonly<{
293
312
  onLoading?: ((_state: LoadStateSlotProps) => any) | undefined;
294
313
  }>, {
295
- options: Omit<HybridRequestOptions, "method" | "url" | "data">;
314
+ options: Omit<HybridRequestOptions, "data" | "method" | "url">;
296
315
  as: string;
297
316
  buffer: number;
298
317
  root: string | Element | null;
@@ -325,6 +344,14 @@ interface BulkSelection<T = any> {
325
344
  /** Excluded records. */
326
345
  except: Set<T>;
327
346
  }
347
+ /**
348
+ * Returns the inclusive range between two records in the given order.
349
+ *
350
+ * This is useful for implementing shift-click bulk selection: keep track of the last selected anchor,
351
+ * then pass the currently visible record identifiers and the clicked target identifier to this helper.
352
+ * If the target is not in the list, an empty range is returned. If the anchor is missing, only the target is returned.
353
+ */
354
+ declare function getBulkSelectionRange<T>(records: readonly T[], anchor: T | undefined, target: T): T[];
328
355
  declare function useBulkSelect<T = any>(): {
329
356
  allSelected: vue.ComputedRef<boolean>;
330
357
  anySelected: vue.ComputedRef<boolean>;
@@ -1052,12 +1079,13 @@ declare const registerHook: typeof registerHook$1;
1052
1079
  //#region src/composables/table.d.ts
1053
1080
  interface Table<T extends Record<string, any> = any, PaginatorKind extends 'cursor' | 'length-aware' | 'simple' = 'length-aware'> {
1054
1081
  id: string;
1055
- keyName: string;
1082
+ keyName: keyof T | null;
1056
1083
  scope?: string;
1057
1084
  columns: Column<T>[];
1058
1085
  inlineActions: InlineAction[];
1059
1086
  bulkActions: BulkAction[];
1060
1087
  records: Array<T>;
1088
+ cells: TableCellRow<T>[];
1061
1089
  paginator: Omit<PaginatorKind extends 'cursor' ? CursorPaginator<T> : (PaginatorKind extends 'simple' ? SimplePaginator<T> : Paginator<T>), 'data'>;
1062
1090
  refinements: Refinements;
1063
1091
  endpoint: string;
@@ -1072,6 +1100,14 @@ interface Column<T extends object = never> {
1072
1100
  /** Metadata of this column. */
1073
1101
  metadata: Record<string, any>;
1074
1102
  }
1103
+ interface TableCell<T extends object = never> {
1104
+ value: any;
1105
+ extra: Record<string, any>;
1106
+ }
1107
+ interface TableCellRow<T extends object = never> {
1108
+ key: RecordIdentifier | null;
1109
+ columns: Partial<Record<keyof T | string, TableCell<T>>>;
1110
+ }
1075
1111
  interface Action {
1076
1112
  /** The name of this action. */
1077
1113
  name: string;
@@ -1094,10 +1130,6 @@ interface BulkActionOptions extends Omit<HybridRequestOptions, 'url'> {
1094
1130
  }
1095
1131
  interface InlineAction extends Action {}
1096
1132
  type RecordIdentifier = string | number;
1097
- type AsRecordTypeWithExtra<T extends Record<string, any>> = { [K in keyof T]: {
1098
- extra: Record<string, any>;
1099
- value: T[K];
1100
- } };
1101
1133
  interface TableDefaultOptions extends AvailableHybridRequestOptions {
1102
1134
  /**
1103
1135
  * Whether to include existing query parameters in the request.
@@ -1113,15 +1145,15 @@ type UseTableNavigationResponse = Promise<_hybridly_core0.NavigationResponse | u
1113
1145
  type ExtractRefValue<T> = T extends {
1114
1146
  value: infer Value;
1115
1147
  } ? Value : T;
1116
- interface UseTableInlineActionItem<RecordType extends Record<string, any>, RecordTypeWithExtra extends Record<string, any>> extends InlineAction {
1148
+ interface UseTableInlineActionItem<RecordType extends Record<string, any>> extends InlineAction {
1117
1149
  /** Executes the action. */
1118
- execute: (record: RecordTypeWithExtra | RecordIdentifier | RecordType) => UseTableNavigationResponse;
1150
+ execute: (record: RecordIdentifier | RecordType) => UseTableNavigationResponse;
1119
1151
  }
1120
1152
  interface UseTableBulkActionItem extends BulkAction {
1121
1153
  /** Executes the action. */
1122
1154
  execute: (options?: BulkActionOptions) => UseTableNavigationResponse;
1123
1155
  }
1124
- interface UseTableColumn<RecordTypeWithExtra extends Record<string, any>> extends Column<RecordTypeWithExtra> {
1156
+ interface UseTableColumn<RecordType extends Record<string, any>> extends Column<RecordType> {
1125
1157
  /** Toggles sorting for this column. */
1126
1158
  toggleSort: (options?: ToggleSortOptions) => UseTableNavigationResponse;
1127
1159
  /** Checks whether the column is being sorted. */
@@ -1135,11 +1167,15 @@ interface UseTableColumn<RecordTypeWithExtra extends Record<string, any>> extend
1135
1167
  /** Checks whether the column is filterable. */
1136
1168
  isFilterable: boolean;
1137
1169
  }
1138
- interface UseTableRecordItem<RecordType extends Record<string, any>, RecordTypeWithExtra extends Record<string, any>> {
1170
+ interface UseTableRecordItem<RecordType extends Record<string, any>> {
1139
1171
  /** The actual record. */
1140
1172
  record: RecordType;
1141
- /** The key of the record. Use this instead of `id`. */
1173
+ /** Local key of the record. Use this as a rendering key. */
1142
1174
  key: RecordIdentifier;
1175
+ /** Server-side key of the record, if any. */
1176
+ recordKey: RecordIdentifier | undefined;
1177
+ /** Whether this record has a server-side key. */
1178
+ hasKey: boolean;
1143
1179
  /** Executes the given inline action. */
1144
1180
  execute: (action: string | InlineAction) => UseTableNavigationResponse;
1145
1181
  /** Gets the available inline actions. */
@@ -1155,11 +1191,11 @@ interface UseTableRecordItem<RecordType extends Record<string, any>, RecordTypeW
1155
1191
  /** Checks whether this record is selected. */
1156
1192
  selected: boolean;
1157
1193
  /** Gets the value of the record for the specified column. */
1158
- value: (column: string | Column<RecordTypeWithExtra>) => any;
1194
+ value: (column: string | Column<RecordType>) => any;
1159
1195
  /** Gets the extra object of the record for the specified column. */
1160
- extra: (column: string | Column<RecordTypeWithExtra>, path: string) => any;
1196
+ extra: (column: string | Column<RecordType>, path: string) => any;
1161
1197
  }
1162
- interface UseTableReturn<T extends Table<any, any>, RecordType extends Record<string, any> = (T extends Table<infer R, any> ? R : any), PaginatorKind extends 'cursor' | 'length-aware' | 'simple' = (T extends Table<any, infer P> ? P : 'length-aware'), RecordTypeWithExtra extends Record<string, any> = AsRecordTypeWithExtra<RecordType>> extends Omit<UseRefinements, 'filters' | 'sorts' | 'filtersKey' | 'sortsKey'> {
1198
+ interface UseTableReturn<T extends Table<any, any>, RecordType extends Record<string, any> = (T extends Table<infer R, any> ? R : any), PaginatorKind extends 'cursor' | 'length-aware' | 'simple' = (T extends Table<any, infer P> ? P : 'length-aware')> extends Omit<UseRefinements, 'filters' | 'sorts' | 'filtersKey' | 'sortsKey'> {
1163
1199
  /** Selects all records. */
1164
1200
  selectAll: () => void;
1165
1201
  /** Deselects all records. */
@@ -1171,7 +1207,7 @@ interface UseTableReturn<T extends Table<any, any>, RecordType extends Record<st
1171
1207
  /** Whether all records on the current page are selected. */
1172
1208
  isPageSelected: boolean;
1173
1209
  /** Checks if the given record is selected. */
1174
- isSelected: (record: RecordTypeWithExtra | RecordType) => boolean;
1210
+ isSelected: (record: RecordType) => boolean;
1175
1211
  /** Whether all records are selected. */
1176
1212
  allSelected: boolean;
1177
1213
  /** Whether any records are selected. */
@@ -1185,33 +1221,33 @@ interface UseTableReturn<T extends Table<any, any>, RecordType extends Record<st
1185
1221
  value: RecordIdentifier;
1186
1222
  };
1187
1223
  /** Toggles selection for the given record. */
1188
- toggle: (record: RecordTypeWithExtra | RecordType, force?: boolean) => void;
1224
+ toggle: (record: RecordType, force?: boolean) => void;
1189
1225
  /** Toggles selection for all records. */
1190
1226
  toggleAll: (force?: boolean) => void;
1191
1227
  /** Selects selection for the given record. */
1192
- select: (record: RecordTypeWithExtra | RecordType) => void;
1228
+ select: (record: RecordType) => void;
1193
1229
  /** Deselects selection for the given record. */
1194
- deselect: (record: RecordTypeWithExtra | RecordType) => void;
1230
+ deselect: (record: RecordType) => void;
1195
1231
  /** List of inline actions for this table. */
1196
- inlineActions: Array<UseTableInlineActionItem<RecordType, RecordTypeWithExtra>>;
1232
+ inlineActions: Array<UseTableInlineActionItem<RecordType>>;
1197
1233
  /** List of bulk actions for this table. */
1198
1234
  bulkActions: Array<UseTableBulkActionItem>;
1199
1235
  /** Executes the given inline action for the given record. */
1200
1236
  executeInlineAction: (action: InlineAction | string, options: {
1201
- record: RecordTypeWithExtra | RecordIdentifier | RecordType;
1237
+ record: RecordIdentifier | RecordType;
1202
1238
  } & Omit<HybridRequestOptions, 'url'>) => UseTableNavigationResponse;
1203
1239
  /** Executes the given bulk action. */
1204
1240
  executeBulkAction: (action: BulkAction | string, options?: Omit<HybridRequestOptions, 'url'> & {
1205
1241
  deselect?: boolean;
1206
1242
  }) => UseTableNavigationResponse;
1207
1243
  /** List of columns for this table. */
1208
- columns: Array<UseTableColumn<RecordTypeWithExtra>>;
1244
+ columns: Array<UseTableColumn<RecordType>>;
1209
1245
  /** List of records for this table. */
1210
1246
  data: RecordType[];
1211
1247
  /** List of records for this table. */
1212
- records: Array<UseTableRecordItem<RecordType, RecordTypeWithExtra>>;
1248
+ records: Array<UseTableRecordItem<RecordType>>;
1213
1249
  /** Paginated meta and links. */
1214
- paginator: PaginatorResult<RecordTypeWithExtra, Table<RecordTypeWithExtra, PaginatorKind>['paginator']>;
1250
+ paginator: PaginatorResult<RecordType, Table<RecordType, PaginatorKind>['paginator']>;
1215
1251
  /** Available filters. */
1216
1252
  filters: ExtractRefValue<UseRefinements['filters']>;
1217
1253
  /** Available sorts. */
@@ -1224,7 +1260,7 @@ interface UseTableReturn<T extends Table<any, any>, RecordType extends Record<st
1224
1260
  /**
1225
1261
  * Provides utilities for working with tables.
1226
1262
  */
1227
- declare function useTable<T extends Table<any, any>, RecordType extends Record<string, any> = (T extends Table<infer R, any> ? R : any), PaginatorKind extends 'cursor' | 'length-aware' | 'simple' = (T extends Table<any, infer P> ? P : 'length-aware'), RecordTypeWithExtra extends Record<string, any> = AsRecordTypeWithExtra<RecordType>>(input: MaybeRefOrGetter<T>, defaultOptions?: TableDefaultOptions): UseTableReturn<T, RecordType, PaginatorKind, RecordTypeWithExtra>;
1263
+ declare function useTable<T extends Table<any, any>, RecordType extends Record<string, any> = (T extends Table<infer R, any> ? R : any), PaginatorKind extends 'cursor' | 'length-aware' | 'simple' = (T extends Table<any, infer P> ? P : 'length-aware')>(input: MaybeRefOrGetter<T>, defaultOptions?: TableDefaultOptions): UseTableReturn<T, RecordType, PaginatorKind>;
1228
1264
  //#endregion
1229
1265
  //#region src/composables/validation.d.ts
1230
1266
  /** Accesses all validation errors grouped by bag name. */
@@ -1322,4 +1358,4 @@ interface SetupArguments {
1322
1358
  payload: Record<string, any>;
1323
1359
  }
1324
1360
  //#endregion
1325
- export { Action, AvailableHybridRequestOptions, AvailableHybridRequestOptionsForFilters, BaseFilterRefinement, BindFilterOptions, BooleanFilterRefinement, BoundBooleanFilterRefinement, BoundCallbackFilterRefinement, BoundDateFilterRefinement, BoundFilterRefinement, BoundSelectFilterRefinement, BoundSortRefinement, BoundTernaryFilterRefinement, BoundTextFilterRefinement, BoundTrashedFilterRefinement, BulkAction, BulkSelection, CallbackFilterRefinement, Column, DateFilterRefinement, DefaultFormOptions, Deferred, FilterOperator, FilterRefinement, Form, type FormProps, FormReturn, type FormSlotProps, type FormSubmitOptions, type InitializeOptions, InlineAction, InternalProperties, MaybeWithData, NumericFilterRefinement, PaginatorResult, RecordIdentifier, Refinements, RouterLink, SelectFilterRefinement, SortDirection, SortRefinement, Table, TableDefaultOptions, TernaryFilterRefinement, TextFilterRefinement, TimeSuggestion, TimeframeSuggestion, ToggleSortOptions, TrashedFilterRefinement, UseRefinements, UseTableBulkActionItem, UseTableColumn, UseTableInlineActionItem, UseTableRecordItem, UseTableReturn, WhenVisible, createPaginator, initializeHybridly, isBooleanFilter, isCallbackFilter, isDateFilter, isSelectFilter, isTernaryFilter, isTextFilter, isTrashedFilter, registerHook, route, router, setProperty, useBackForward, useBulkSelect, useDialog, useForm, useHistoryState, useProperties, useProperty, useQueryParameter, useQueryParameters, useRefinements, useRoute, useTable, useValidation, useValidationBag };
1361
+ export { Action, AvailableHybridRequestOptions, AvailableHybridRequestOptionsForFilters, BaseFilterRefinement, BindFilterOptions, BooleanFilterRefinement, BoundBooleanFilterRefinement, BoundCallbackFilterRefinement, BoundDateFilterRefinement, BoundFilterRefinement, BoundSelectFilterRefinement, BoundSortRefinement, BoundTernaryFilterRefinement, BoundTextFilterRefinement, BoundTrashedFilterRefinement, BulkAction, BulkSelection, CallbackFilterRefinement, Column, DateFilterRefinement, DefaultFormOptions, Deferred, FilterOperator, FilterRefinement, Form, type FormProps, FormReturn, type FormSlotProps, type FormSubmitOptions, type InitializeOptions, InlineAction, InternalProperties, MaybeWithData, NumericFilterRefinement, PaginatorResult, RecordIdentifier, Refinements, RouterLink, SelectFilterRefinement, SortDirection, SortRefinement, Table, TableCell, TableCellRow, TableDefaultOptions, TernaryFilterRefinement, TextFilterRefinement, TimeSuggestion, TimeframeSuggestion, ToggleSortOptions, TrashedFilterRefinement, UseRefinements, UseTableBulkActionItem, UseTableColumn, UseTableInlineActionItem, UseTableRecordItem, UseTableReturn, WhenVisible, createPaginator, getBulkSelectionRange, initializeHybridly, isBooleanFilter, isCallbackFilter, isDateFilter, isSelectFilter, isTernaryFilter, isTextFilter, isTrashedFilter, registerHook, route, router, setProperty, useBackForward, useBulkSelect, useDialog, useForm, useHistoryState, useProperties, useProperty, useQueryParameter, useQueryParameters, useRefinements, useRoute, useTable, useValidation, useValidationBag };
package/dist/index.mjs CHANGED
@@ -3,11 +3,11 @@ import { debug, merge, random, showViewComponentErrorModal, wrap } from "@hybrid
3
3
  import { computed, createApp, defineComponent, getCurrentInstance, h, isRef, nextTick, onMounted, onUnmounted, reactive, readonly, ref, shallowRef, toRaw, toValue, triggerRef, unref, watch } from "vue";
4
4
  import { createRouter, definePlugin, makeUrl, parseQueryString, registerHook as registerHook$1, route, route as route$1, router, router as router$1, stringifyQueryString } from "@hybridly/core";
5
5
  import { get, set, unset } from "es-toolkit/compat";
6
+ import { debounce } from "es-toolkit/function";
6
7
  import { clone, cloneDeep } from "es-toolkit/object";
7
8
  import { isEqual } from "es-toolkit/predicate";
8
9
  import { setupDevtoolsPlugin } from "@vue/devtools-api";
9
10
  import nprogress from "nprogress";
10
- import { debounce } from "es-toolkit/function";
11
11
  import { getByPath } from "@clickbar/dot-diver";
12
12
  //#region src/stores/state.ts
13
13
  const state = {
@@ -216,7 +216,7 @@ function useForm(options) {
216
216
  * Submits the form.
217
217
  */
218
218
  function submit(optionsOverrides) {
219
- const { fields: _f, key: _k, ...optionsWithoutFields } = options;
219
+ const { fields: _f, key: _k, automaticallySubmit: _as, ...optionsWithoutFields } = options;
220
220
  const resolvedOptions = optionsOverrides ? merge(optionsWithoutFields, optionsOverrides, { mergePlainObjects: true }) : optionsWithoutFields;
221
221
  const { timeout, resetOnSuccess, resetOnError, setDefaultOnSuccess, transform, ...requestOptions } = merge(formStore.getDefaultConfig(), resolvedOptions, { mergePlainObjects: true });
222
222
  const url = typeof requestOptions.url === "function" ? requestOptions.url() : requestOptions.url;
@@ -274,6 +274,23 @@ function useForm(options) {
274
274
  }
275
275
  });
276
276
  }
277
+ if (options.automaticallySubmit) {
278
+ const submitOptions = options.automaticallySubmit === true ? void 0 : (() => {
279
+ const { debounce: _debounce, ...submitOptions } = options.automaticallySubmit;
280
+ return submitOptions;
281
+ })();
282
+ const automaticallySubmitOptions = options.automaticallySubmit === true ? {
283
+ debounce: 100,
284
+ immediate: true
285
+ } : options.automaticallySubmit;
286
+ watch(() => fields, debounce(() => {
287
+ if (!isDirty.value) return;
288
+ submit(submitOptions);
289
+ }, automaticallySubmitOptions.debounce ?? 100), {
290
+ deep: true,
291
+ immediate: automaticallySubmitOptions.immediate ?? true
292
+ });
293
+ }
277
294
  /**
278
295
  * Clears all errors.
279
296
  */
@@ -1373,6 +1390,21 @@ function useBackForward(options) {
1373
1390
  }
1374
1391
  //#endregion
1375
1392
  //#region src/composables/bulk-select.ts
1393
+ /**
1394
+ * Returns the inclusive range between two records in the given order.
1395
+ *
1396
+ * This is useful for implementing shift-click bulk selection: keep track of the last selected anchor,
1397
+ * then pass the currently visible record identifiers and the clicked target identifier to this helper.
1398
+ * If the target is not in the list, an empty range is returned. If the anchor is missing, only the target is returned.
1399
+ */
1400
+ function getBulkSelectionRange(records, anchor, target) {
1401
+ const targetIndex = records.indexOf(target);
1402
+ if (targetIndex === -1) return [];
1403
+ if (anchor === void 0) return [target];
1404
+ const anchorIndex = records.indexOf(anchor);
1405
+ if (anchorIndex === -1) return [target];
1406
+ return records.slice(Math.min(anchorIndex, targetIndex), Math.max(anchorIndex, targetIndex) + 1);
1407
+ }
1376
1408
  function useBulkSelect() {
1377
1409
  const selection = ref({
1378
1410
  all: false,
@@ -1921,11 +1953,24 @@ function useTable(input, defaultOptions = {}) {
1921
1953
  */
1922
1954
  function getRecordKey(record) {
1923
1955
  if (typeof record !== "object") return record;
1924
- if (Reflect.has(record, "__hybridId")) return Reflect.get(record, "__hybridId");
1925
- if (!table.value.keyName) throw new Error("Record key cannot be fetched because the table has no defined key.");
1956
+ if (!table.value.keyName) return;
1926
1957
  const value = Reflect.get(record, table.value.keyName);
1927
- if (typeof value === "object" && Reflect.has(value, "value")) return value.value;
1928
- return value;
1958
+ if (typeof value === "string" || typeof value === "number") return value;
1959
+ }
1960
+ function getPageRecordKeys() {
1961
+ return table.value.records.map((record) => getRecordKey(record)).filter((key) => key !== void 0);
1962
+ }
1963
+ function warnMissingRecordKey(action) {
1964
+ console.warn(`Cannot ${action} because this table record has no key.`);
1965
+ }
1966
+ function getColumnName(column) {
1967
+ return typeof column === "string" ? column : column.name;
1968
+ }
1969
+ function getCell(index, column) {
1970
+ return table.value.cells[index]?.columns[getColumnName(column)] ?? {
1971
+ value: void 0,
1972
+ extra: {}
1973
+ };
1929
1974
  }
1930
1975
  function resolveInlineAction(action) {
1931
1976
  if (typeof action !== "string") return action;
@@ -1944,10 +1989,15 @@ function useTable(input, defaultOptions = {}) {
1944
1989
  */
1945
1990
  async function executeInlineAction(action, options) {
1946
1991
  const resolvedAction = resolveInlineAction(action);
1992
+ const recordKey = getRecordKey(options.record);
1947
1993
  if (!resolvedAction) {
1948
1994
  console.warn(`Action [${action}] is not defined`);
1949
1995
  return;
1950
1996
  }
1997
+ if (recordKey === void 0) {
1998
+ warnMissingRecordKey("execute an inline action");
1999
+ return;
2000
+ }
1951
2001
  return await router$1.navigate({
1952
2002
  method: "post",
1953
2003
  url: getActionUrl(resolvedAction, table.value),
@@ -1957,7 +2007,7 @@ function useTable(input, defaultOptions = {}) {
1957
2007
  type: "action:inline",
1958
2008
  action: resolvedAction.name,
1959
2009
  tableId: table.value.id,
1960
- recordId: getRecordKey(options.record)
2010
+ recordId: recordKey
1961
2011
  }
1962
2012
  });
1963
2013
  }
@@ -1996,20 +2046,35 @@ function useTable(input, defaultOptions = {}) {
1996
2046
  });
1997
2047
  }
1998
2048
  return reactive({
1999
- selectAll: bulk.selectAll,
2049
+ selectAll: () => table.value.keyName ? bulk.selectAll() : warnMissingRecordKey("select all records"),
2000
2050
  deselectAll: bulk.deselectAll,
2001
- selectPage: () => bulk.select(...table.value.records.map((record) => getRecordKey(record))),
2002
- deselectPage: () => bulk.deselect(...table.value.records.map((record) => getRecordKey(record))),
2003
- isPageSelected: computed(() => table.value.records.length > 0 && table.value.records.every((record) => bulk.selected(getRecordKey(record)))),
2004
- isSelected: (record) => bulk.selected(getRecordKey(record)),
2051
+ selectPage: () => bulk.select(...getPageRecordKeys()),
2052
+ deselectPage: () => bulk.deselect(...getPageRecordKeys()),
2053
+ isPageSelected: computed(() => {
2054
+ const keys = getPageRecordKeys();
2055
+ return keys.length > 0 && keys.every((key) => bulk.selected(key));
2056
+ }),
2057
+ isSelected: (record) => {
2058
+ const key = getRecordKey(record);
2059
+ return key === void 0 ? false : bulk.selected(key);
2060
+ },
2005
2061
  allSelected: bulk.allSelected,
2006
2062
  anySelected: bulk.anySelected,
2007
2063
  selection: bulk.selection,
2008
2064
  bindCheckbox: (key) => bulk.bindCheckbox(key),
2009
- toggle: (record, force) => bulk.toggle(getRecordKey(record), force),
2010
- toggleAll: (force) => bulk.toggleAll(force),
2011
- select: (record) => bulk.select(getRecordKey(record)),
2012
- deselect: (record) => bulk.deselect(getRecordKey(record)),
2065
+ toggle: (record, force) => {
2066
+ const key = getRecordKey(record);
2067
+ return key === void 0 ? warnMissingRecordKey("toggle record selection") : bulk.toggle(key, force);
2068
+ },
2069
+ toggleAll: (force) => table.value.keyName ? bulk.toggleAll(force) : warnMissingRecordKey("toggle all records"),
2070
+ select: (record) => {
2071
+ const key = getRecordKey(record);
2072
+ return key === void 0 ? warnMissingRecordKey("select this record") : bulk.select(key);
2073
+ },
2074
+ deselect: (record) => {
2075
+ const key = getRecordKey(record);
2076
+ return key === void 0 ? warnMissingRecordKey("deselect this record") : bulk.deselect(key);
2077
+ },
2013
2078
  inlineActions: computed(() => table.value.inlineActions.map((action) => ({
2014
2079
  execute: (record) => executeInlineAction(action, { record }),
2015
2080
  ...action
@@ -2029,29 +2094,27 @@ function useTable(input, defaultOptions = {}) {
2029
2094
  isSortable: !!refinements.sorts.value.find((sort) => sort.name === column.name),
2030
2095
  isFilterable: !!refinements.filters.value.find((filters) => filters.name === column.name)
2031
2096
  }))),
2032
- data: computed(() => {
2033
- return table.value.records.map((record) => {
2034
- const entries = Object.entries(record).map(([key, value]) => [key, value.value]).filter(([key]) => key !== "__hybridId");
2035
- if (entries.length === 0) return;
2036
- return Object.fromEntries(entries);
2037
- }).filter(Boolean);
2038
- }),
2039
- records: computed(() => table.value.records.map((record) => {
2040
- const entries = Object.entries(record).map(([key, value]) => [key, value.value]).filter(([key]) => key !== "__hybridId");
2097
+ data: computed(() => table.value.records),
2098
+ records: computed(() => table.value.records.map((record, index) => {
2099
+ const recordKey = getRecordKey(record);
2100
+ const localKey = recordKey ?? `row-${index}`;
2101
+ const selected = recordKey === void 0 ? false : bulk.selected(recordKey);
2041
2102
  return {
2042
- record: entries.length > 0 ? Object.fromEntries(entries) : {},
2043
- key: getRecordKey(record),
2044
- execute: (action) => executeInlineAction(action, { record: getRecordKey(record) }),
2103
+ record,
2104
+ key: localKey,
2105
+ recordKey,
2106
+ hasKey: recordKey !== void 0,
2107
+ execute: (action) => executeInlineAction(action, { record }),
2045
2108
  actions: table.value.inlineActions.map((action) => ({
2046
2109
  ...action,
2047
- execute: () => executeInlineAction(action.name, { record: getRecordKey(record) })
2110
+ execute: () => executeInlineAction(action.name, { record })
2048
2111
  })),
2049
- select: () => bulk.select(getRecordKey(record)),
2050
- deselect: () => bulk.deselect(getRecordKey(record)),
2051
- toggle: (force) => bulk.toggle(getRecordKey(record), force),
2052
- selected: bulk.selected(getRecordKey(record)),
2053
- value: (column) => record[typeof column === "string" ? column : column.name].value,
2054
- extra: (column, path) => getByPath(record[typeof column === "string" ? column : column.name].extra, path)
2112
+ select: () => recordKey === void 0 ? warnMissingRecordKey("select this record") : bulk.select(recordKey),
2113
+ deselect: () => recordKey === void 0 ? warnMissingRecordKey("deselect this record") : bulk.deselect(recordKey),
2114
+ toggle: (force) => recordKey === void 0 ? warnMissingRecordKey("toggle record selection") : bulk.toggle(recordKey, force),
2115
+ selected,
2116
+ value: (column) => getCell(index, column).value,
2117
+ extra: (column, path) => getByPath(getCell(index, column).extra, path)
2055
2118
  };
2056
2119
  })),
2057
2120
  paginator: computed(() => createPaginator(table.value.paginator, defaultOptions)),
@@ -2069,4 +2132,4 @@ function useValidationBag(bag = "default") {
2069
2132
  return readonly(toReactive(computed(() => state.context.value?.validation?.[bag] ?? {})));
2070
2133
  }
2071
2134
  //#endregion
2072
- export { Deferred, Form, RouterLink, WhenVisible, createPaginator, initializeHybridly, isBooleanFilter, isCallbackFilter, isDateFilter, isSelectFilter, isTernaryFilter, isTextFilter, isTrashedFilter, registerHook, route, router, setProperty, useBackForward, useBulkSelect, useDialog, useForm, useHistoryState, useProperties, useProperty, useQueryParameter, useQueryParameters, useRefinements, useRoute, useTable, useValidation, useValidationBag };
2135
+ export { Deferred, Form, RouterLink, WhenVisible, createPaginator, getBulkSelectionRange, initializeHybridly, isBooleanFilter, isCallbackFilter, isDateFilter, isSelectFilter, isTernaryFilter, isTextFilter, isTrashedFilter, registerHook, route, router, setProperty, useBackForward, useBulkSelect, useDialog, useForm, useHistoryState, useProperties, useProperty, useQueryParameter, useQueryParameters, useRefinements, useRoute, useTable, useValidation, useValidationBag };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hybridly/vue",
3
3
  "type": "module",
4
- "version": "0.10.0-beta.26",
4
+ "version": "0.10.0-beta.27",
5
5
  "description": "Vue adapter for Hybridly",
6
6
  "author": "Enzo Innocenzi <enzo@innocenzi.dev>",
7
7
  "license": "MIT",
@@ -47,8 +47,8 @@
47
47
  "dependencies": {
48
48
  "@clickbar/dot-diver": "^1.0.7",
49
49
  "es-toolkit": "^1.45.1",
50
- "@hybridly/core": "0.10.0-beta.26",
51
- "@hybridly/utils": "0.10.0-beta.26",
50
+ "@hybridly/core": "0.10.0-beta.27",
51
+ "@hybridly/utils": "0.10.0-beta.27",
52
52
  "@vue/devtools-api": "^8.1.0",
53
53
  "defu": "^6.1.4",
54
54
  "nprogress": "^0.2.0"