@lavalogic/scoria 0.7.2 → 0.8.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.
@@ -23,14 +23,18 @@
23
23
  isValid?: (item: V | null) => boolean;
24
24
  }
25
25
 
26
- export interface QuerySelectPropsAsObject<V extends object, VP extends ValueProp<V>>
27
- extends QuerySelectPropsAsObjectCommonProps<V, VP> {
26
+ export interface QuerySelectPropsAsObject<
27
+ V extends object,
28
+ VP extends ValueProp<V>
29
+ > extends QuerySelectPropsAsObjectCommonProps<V, VP> {
28
30
  valueAsObject: true;
29
31
  value: V | null;
30
32
  }
31
33
 
32
- export interface QuerySelectPropsAsPrimitive<V extends object, VP extends ValueProp<V>>
33
- extends QuerySelectPropsAsObjectCommonProps<V, VP> {
34
+ export interface QuerySelectPropsAsPrimitive<
35
+ V extends object,
36
+ VP extends ValueProp<V>
37
+ > extends QuerySelectPropsAsObjectCommonProps<V, VP> {
34
38
  valueAsObject: false;
35
39
  value: V[VP] | null;
36
40
  options: V[];
@@ -312,7 +316,7 @@
312
316
  forceShowDropdown(e);
313
317
  onfocus?.(e);
314
318
  }}
315
- placeholder="Please Select {name ? `${an} ${name}` : ''}"
319
+ placeholder="Please Select{name ? ` ${an} ${name}` : ''}"
316
320
  bind:this={ref}
317
321
  {inputId}
318
322
  {disabled}
@@ -62,8 +62,10 @@
62
62
  );
63
63
 
64
64
  const isSearchable = $derived(
65
- hasFilterFn || hasAcceptableColumnType || customFilters?.has(def.id) == true
65
+ def.allowFiltering != false &&
66
+ ((hasFilterFn && hasAcceptableColumnType) || customFilters?.has(def.id) == true)
66
67
  );
68
+
67
69
  const isNumeric = $derived(
68
70
  def instanceof NumberInputDef ||
69
71
  def.filterValueType == 'Number' ||
@@ -29,9 +29,7 @@
29
29
  <script lang="ts" generics="T extends object,">
30
30
  const { cell }: TableQuerySelectProps<T> = $props();
31
31
 
32
- const value = $derived(
33
- (cell.getValue() ?? null) as SelectOption<unknown> | Promise<SelectOption<unknown>> | null
34
- );
32
+ const value = $derived((cell.getValue() ?? null) as unknown | Promise<unknown> | null);
35
33
 
36
34
  const visibleValue = $derived(
37
35
  cell.column.columnDef.getDisplayValue(cell.row.original, cell.row.originalIndex)
@@ -52,26 +50,65 @@
52
50
  );
53
51
  let editable = $state(false);
54
52
 
55
- let asyncCounter = 0;
53
+ let valueAsyncCounter = 0;
54
+ $effect(() => {
55
+ void value;
56
+ void lastOptions;
57
+
58
+ untrack(() => {
59
+ const localCounter = (valueAsyncCounter = valueAsyncCounter + 1);
60
+ (async () => {
61
+ try {
62
+ const awaitedValue = await value;
63
+ const label = (await visibleValue) ?? awaitedValue?.toString();
64
+
65
+ if (awaitedValue != null && valueAsyncCounter === localCounter) {
66
+ if (label == null) {
67
+ throw new Error('invalid label and value');
68
+ }
69
+ if (!lastOptions.some((it) => it.value == awaitedValue)) {
70
+ lastOptions.push({
71
+ label,
72
+ value: awaitedValue
73
+ });
74
+ }
75
+ } else if (dev || columnDef.debug) {
76
+ console.log('skipping async option insertion because it is stale');
77
+ }
78
+ } catch (e) {
79
+ if (dev || columnDef.debug) {
80
+ console.error(e);
81
+ }
82
+ if (valueAsyncCounter === localCounter) {
83
+ //nothing happens
84
+ } else if (dev || columnDef.debug) {
85
+ console.log('skipping caught async option insertion error because it is stale');
86
+ }
87
+ }
88
+ })();
89
+ });
90
+ });
91
+
92
+ let editableAsyncCounter = 0;
56
93
  $effect(() => {
57
94
  void editableAsync;
58
95
  untrack(() => {
59
- const localCounter = (asyncCounter = asyncCounter + 1);
96
+ const localCounter = (editableAsyncCounter = editableAsyncCounter + 1);
60
97
  (async () => {
61
98
  try {
62
99
  const isEditable = await editableAsync;
63
- if (asyncCounter === localCounter) {
100
+ if (editableAsyncCounter === localCounter) {
64
101
  editable = isEditable;
65
- } else if (dev) {
102
+ } else if (dev || columnDef.debug) {
66
103
  console.log('skipping async editable because it is stale');
67
104
  }
68
105
  } catch (e) {
69
- if (dev) {
106
+ if (dev || columnDef.debug) {
70
107
  console.error(e);
71
108
  }
72
- if (asyncCounter === localCounter) {
109
+ if (editableAsyncCounter === localCounter) {
73
110
  editable = false;
74
- } else if (dev) {
111
+ } else if (dev || columnDef.debug) {
75
112
  console.log('skipping async editable because it is stale');
76
113
  }
77
114
  }
@@ -93,6 +130,7 @@
93
130
  );
94
131
 
95
132
  let isBeingEdited = $state(false);
133
+
96
134
  $effect(() => {
97
135
  if (wantsToBeEdited) {
98
136
  if (editable) {
@@ -237,11 +275,13 @@
237
275
  let prevtdref: HTMLTableCellElement | undefined = undefined;
238
276
  $effect(() => {
239
277
  if (tdref) {
240
- if (tdref != prevtdref) {
241
- tdref.addEventListener('edit', handleTDFocus);
242
- } else {
243
- prevtdref = tdref;
244
- }
278
+ untrack(() => {
279
+ if (tdref != prevtdref) {
280
+ tdref!.addEventListener('edit', handleTDFocus);
281
+ } else {
282
+ prevtdref = tdref;
283
+ }
284
+ });
245
285
  }
246
286
  });
247
287
  </script>
@@ -256,14 +296,14 @@
256
296
  focusOnLoad
257
297
  name={cell.column.columnDef.id}
258
298
  valueProp="value"
259
- clearable={cell.column.columnDef.clearable ?? true}
260
299
  labelProp="label"
261
- valueAsObject={columnDef.valueAsObject}
300
+ clearable={cell.column.columnDef.clearable ?? true}
301
+ valueAsObject={false}
262
302
  {value}
263
303
  query={(v) => columnDef.query(v, cell.row.original, cell.row.originalIndex)}
264
304
  bind:options={lastOptions}
265
305
  onfocus={addKeydownEvent}
266
- onchange={(newValue) => {
306
+ onchange={(newValue: SelectOption<unknown> | null) => {
267
307
  if (newValue?.value !== value) {
268
308
  tableContext.onEditCell(
269
309
  cell.row.original,
@@ -11,6 +11,7 @@ import { DisplayDef } from '../Types/Columns/Definitions/Display/DisplayDef.svel
11
11
  import { ProgressBarDef } from '../Types/Columns/Definitions/Display/ProgressBarDef.svelte.js';
12
12
  import { ValidityDef } from '../Types/Columns/Definitions/Display/ValidityDef.svelte.js';
13
13
  import { asyncSleep } from '../../../Helpers/Helpers.svelte.js';
14
+ import { QueryDef } from '../Types/Columns/Definitions/Accessors/QueryDef.svelte.js';
14
15
  export class ExampleItemColumnDef {
15
16
  static generateExampleItemColumnDefs(modal) {
16
17
  let defs = $state([
@@ -39,6 +40,25 @@ export class ExampleItemColumnDef {
39
40
  id: 'id',
40
41
  filterValueType: 'Number'
41
42
  }),
43
+ new QueryDef({
44
+ header: 'simulated query def',
45
+ getDisplayValue: async (item, index) => {
46
+ return item.simulatedQuery?.value ?? null;
47
+ },
48
+ isEditable: () => true,
49
+ id: 'simulatedQuery',
50
+ query: async (v, item, index) => {
51
+ asyncSleep(25);
52
+ return item.someDate
53
+ ? new Array(5).fill(0).map((it, index) => {
54
+ return {
55
+ label: (it + index).toString(),
56
+ value: { value: (it + index).toString() }
57
+ };
58
+ })
59
+ : [];
60
+ }
61
+ }),
42
62
  new TextInputDef({
43
63
  header: 'Name',
44
64
  id: 'name',
@@ -106,7 +106,8 @@
106
106
  );
107
107
 
108
108
  const isSearchable = $derived(
109
- hasFilterFn || hasAcceptableColumnType || (customFilters?.has(def.id) ?? false)
109
+ def.allowFiltering != false &&
110
+ ((hasFilterFn && hasAcceptableColumnType) || customFilters?.has(def.id) == true)
110
111
  );
111
112
 
112
113
  const hasQuery = $derived(isRemoteSelect && customFilter?.query);
@@ -4,7 +4,6 @@ import type { FilterFn, SelectFilterValueType } from '../../../Filtering/FilterF
4
4
  import type { ColumnDefProps } from '../ColumnDef.svelte.js';
5
5
  import { AccessorDef } from './AccessorDef.svelte.js';
6
6
  export interface QueryDefProps<T extends object> extends ColumnDefProps<T, SelectFilterValueType<T>> {
7
- valueAsObject: boolean;
8
7
  query: (queryString: string, item: T, index: number) => Promise<Array<SelectOption<unknown>>>;
9
8
  getDisplayValue: (item: T, index: number) => string | null | Promise<string | null>;
10
9
  clearable?: boolean;
@@ -13,7 +12,6 @@ export declare class QueryDef<T extends object> extends AccessorDef<T, SelectFil
13
12
  readonly accessorType: "Query";
14
13
  constructor(props: QueryDefProps<T>);
15
14
  readonly filterFn: CustomFilterFn | FilterFn<T, SelectFilterValueType<T>> | undefined;
16
- readonly valueAsObject: boolean;
17
15
  get getDisplayValue(): (item: T, index: number) => string | null | Promise<string | null>;
18
16
  readonly query: (queryString: string, item: T, index: number) => Promise<Array<SelectOption<unknown>>>;
19
17
  readonly clearable: boolean;
@@ -4,12 +4,10 @@ export class QueryDef extends AccessorDef {
4
4
  accessorType = AccessorType.Query;
5
5
  constructor(props) {
6
6
  super(props);
7
- this.valueAsObject = $derived(props.valueAsObject);
8
7
  this.query = $derived(props.query);
9
8
  this.clearable = $derived(props.clearable ?? true);
10
9
  }
11
10
  filterFn;
12
- valueAsObject;
13
11
  get getDisplayValue() {
14
12
  return this._getDisplayValue;
15
13
  }
@@ -1918,9 +1918,9 @@ export class TableContext {
1918
1918
  this.assertNoDuplicateIds();
1919
1919
  const adjustedDefsPromises = this.defaultColumnDefs.defs.map(async (def) => {
1920
1920
  if (def instanceof AccessorDef) {
1921
- // if (hasNoDefaultAccessor(temporary)) {
1922
- // temporary.accessorKey = def.id as string & keyof T;
1923
- // }
1921
+ if (def.allowFiltering == false) {
1922
+ return def;
1923
+ }
1924
1924
  if (!('minSize' in def) || !def.minSize) {
1925
1925
  if (typeof def.header === 'string') {
1926
1926
  const minWidth = this.measureHeaderWidth(def);
@@ -1,4 +1,5 @@
1
1
  import type { SQLDate } from '../Internal/Misc.js';
2
+ import type { SelectOption } from '../Internal/SelectOption.js';
2
3
  import type { ExampleSelectEnum } from './ExampleSelectEnum.js';
3
4
  export interface ExampleItem {
4
5
  id: number;
@@ -12,4 +13,5 @@ export interface ExampleItem {
12
13
  floDateWithTime: SQLDate | null;
13
14
  someSelect: ExampleSelectEnum | null;
14
15
  someBubbles: string[];
16
+ simulatedQuery: SelectOption<string> | null;
15
17
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lavalogic/scoria",
3
3
  "description": "Svelte components used for the FloWMS Web Frontend",
4
- "version": "0.7.2",
4
+ "version": "0.8.0",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },
@@ -33,19 +33,19 @@
33
33
  "@eslint/compat": "^2.0.0",
34
34
  "@eslint/js": "^9.39.2",
35
35
  "@playwright/test": "^1.57.0",
36
- "@storybook/addon-a11y": "^10.1.10",
37
- "@storybook/addon-docs": "^10.1.10",
36
+ "@storybook/addon-a11y": "^10.1.11",
37
+ "@storybook/addon-docs": "^10.1.11",
38
38
  "@storybook/addon-svelte-csf": "^5.0.10",
39
- "@storybook/addon-vitest": "^10.1.10",
40
- "@storybook/sveltekit": "^10.1.10",
39
+ "@storybook/addon-vitest": "^10.1.11",
40
+ "@storybook/sveltekit": "^10.1.11",
41
41
  "@sveltejs/adapter-node": "^5.4.0",
42
- "@sveltejs/kit": "^2.49.2",
42
+ "@sveltejs/kit": "^2.49.3",
43
43
  "@sveltejs/package": "^2.5.7",
44
- "@sveltejs/vite-plugin-svelte": "^6.2.1",
44
+ "@sveltejs/vite-plugin-svelte": "^6.2.2",
45
45
  "@types/eslint": "^9.6.1",
46
46
  "@types/node": "^24.10.4",
47
- "@typescript-eslint/eslint-plugin": "^8.50.0",
48
- "@typescript-eslint/parser": "^8.50.0",
47
+ "@typescript-eslint/eslint-plugin": "^8.52.0",
48
+ "@typescript-eslint/parser": "^8.52.0",
49
49
  "@vitest/browser": "^4.0.16",
50
50
  "eslint": "^9.39.2",
51
51
  "eslint-config-prettier": "^10.1.8",
@@ -57,13 +57,13 @@
57
57
  "prettier": "^3.7.4",
58
58
  "prettier-plugin-svelte": "^3.4.1",
59
59
  "publint": "^0.3.16",
60
- "sass-embedded": "^1.97.0",
61
- "storybook": "^10.1.10",
62
- "svelte": "^5.46.0",
63
- "svelte-check": "^4.3.4",
60
+ "sass-embedded": "^1.97.2",
61
+ "storybook": "^10.1.11",
62
+ "svelte": "^5.46.1",
63
+ "svelte-check": "^4.3.5",
64
64
  "svelte-render-scan": "^1.1.0",
65
65
  "typescript": "^5.9.3",
66
- "typescript-eslint": "^8.50.0",
66
+ "typescript-eslint": "^8.52.0",
67
67
  "vite": "^7.3.0",
68
68
  "vite-plugin-devtools-json": "^1.0.0",
69
69
  "vitest": "^4.0.16",
@@ -74,7 +74,7 @@
74
74
  ],
75
75
  "dependencies": {
76
76
  "svelecte": "^5.3.0",
77
- "svelte-dnd-action": "^0.9.68"
77
+ "svelte-dnd-action": "^0.9.69"
78
78
  },
79
79
  "scripts": {
80
80
  "dev": "vite dev",