@lavalogic/scoria 0.22.5 → 0.23.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.
@@ -1,4 +1,4 @@
1
1
  import type { DimensionInputProps } from './DimensionInputProps.js';
2
- declare const DimensionInput: import("svelte").Component<DimensionInputProps, {}, "x" | "z" | "y" | "inputRef">;
2
+ declare const DimensionInput: import("svelte").Component<DimensionInputProps, {}, "x" | "y" | "z" | "inputRef">;
3
3
  type DimensionInput = ReturnType<typeof DimensionInput>;
4
4
  export default DimensionInput;
@@ -1,6 +1,9 @@
1
1
  <svelte:options runes />
2
2
 
3
- <script lang="ts" generics="Args">
3
+ <script
4
+ lang="ts"
5
+ generics="Args"
6
+ >
4
7
  import { devCatch } from '../Helpers/Helpers.svelte.js';
5
8
  import { Colour } from '../scss/colours.js';
6
9
 
@@ -11,6 +14,8 @@
11
14
 
12
15
  let { option, selected }: TabGroupButtonProps<Args> = $props();
13
16
 
17
+ let disabled = $state(true);
18
+ let visible = $state(true);
14
19
  let isValid = $state(true);
15
20
 
16
21
  $effect(() => {
@@ -24,6 +29,33 @@
24
29
  .catch(devCatch);
25
30
  }
26
31
  });
32
+ $effect(() => {
33
+ if (typeof option.disabled === 'boolean') {
34
+ disabled = option.disabled;
35
+ } else if (option.disabled == undefined) {
36
+ disabled = false;
37
+ } else {
38
+ option.disabled
39
+ .then((v) => {
40
+ disabled = v;
41
+ })
42
+ .catch(devCatch);
43
+ }
44
+ });
45
+
46
+ $effect(() => {
47
+ if (typeof option.visible === 'boolean') {
48
+ visible = option.visible;
49
+ } else if (option.visible == undefined) {
50
+ visible = false;
51
+ } else {
52
+ option.visible
53
+ .then((v) => {
54
+ visible = v;
55
+ })
56
+ .catch(devCatch);
57
+ }
58
+ });
27
59
  </script>
28
60
 
29
61
  <button
@@ -34,12 +66,23 @@
34
66
  type="button"
35
67
  class="tab"
36
68
  class:invalid={!isValid}
69
+ {disabled}
70
+ class:hidden={!visible}
37
71
  >
38
- <Icon name="nothing" colour={Colour['$brand-primary']} size={Size.Small} {...option.iconLeft} />
72
+ <Icon
73
+ name="nothing"
74
+ colour={Colour['$brand-primary']}
75
+ size={Size.Small}
76
+ {...option.iconLeft}
77
+ />
39
78
 
40
79
  {option.label}
41
80
  {#if !isValid}
42
- <Icon name="error" colour={Colour['$error-border']} size={Size.Small} />
81
+ <Icon
82
+ name="error"
83
+ colour={Colour['$error-border']}
84
+ size={Size.Small}
85
+ />
43
86
  {:else}
44
87
  <Icon
45
88
  name="nothing"
@@ -50,7 +93,11 @@
50
93
  {/if}
51
94
  </button>
52
95
 
53
- <style>.tab {
96
+ <style>button.tab.hidden {
97
+ display: none !important;
98
+ }
99
+
100
+ .tab {
54
101
  color: #3a4952;
55
102
  cursor: pointer;
56
103
  font-size: 1rem;
@@ -1,6 +1,9 @@
1
1
  <svelte:options runes />
2
2
 
3
- <script lang="ts" module>
3
+ <script
4
+ lang="ts"
5
+ module
6
+ >
4
7
  interface QuerySelectPropsAsObjectCommonProps<V extends object, VP extends ValueProp<V>> {
5
8
  labelProp: NonNullableKey<V>;
6
9
  valueProp: VP;
@@ -25,7 +28,7 @@
25
28
 
26
29
  export interface QuerySelectPropsAsObject<
27
30
  V extends object,
28
- VP extends ValueProp<V>
31
+ VP extends ValueProp<V>,
29
32
  > extends QuerySelectPropsAsObjectCommonProps<V, VP> {
30
33
  valueAsObject: true;
31
34
  value: V | null;
@@ -33,11 +36,10 @@
33
36
 
34
37
  export interface QuerySelectPropsAsPrimitive<
35
38
  V extends object,
36
- VP extends ValueProp<V>
39
+ VP extends ValueProp<V>,
37
40
  > extends QuerySelectPropsAsObjectCommonProps<V, VP> {
38
41
  valueAsObject: false;
39
42
  value: V[VP] | null;
40
- options: Array<V>;
41
43
  }
42
44
 
43
45
  export type QuerySelectProps<V extends object, VP extends ValueProp<V>> =
@@ -46,7 +48,10 @@
46
48
  </script>
47
49
 
48
50
  <!-- #region Script -->
49
- <script lang="ts" generics="V extends object, VP extends ValueProp<V>">
51
+ <script
52
+ lang="ts"
53
+ generics="V extends object, VP extends ValueProp<V>"
54
+ >
50
55
  import { ToastContext } from '../Contexts/ToastContext.svelte.js';
51
56
 
52
57
  import { dev } from '$app/environment';
@@ -77,7 +82,7 @@
77
82
  options = $bindable<Array<V>>([]),
78
83
  isValid,
79
84
  onchange,
80
- onfocus
85
+ onfocus,
81
86
  }: QuerySelectProps<V, VP> = $props();
82
87
 
83
88
  export function forceQuery() {
@@ -120,7 +125,7 @@
120
125
  //best-effort solution. Not a good one
121
126
  // eslint-disable-next-line @typescript-eslint/no-base-to-string
122
127
  [labelProp]: selectedValue.toString(),
123
- [valueProp]: selectedValue
128
+ [valueProp]: selectedValue,
124
129
  } as V);
125
130
  }
126
131
  }
@@ -372,7 +377,7 @@
372
377
  }}
373
378
  icon={{
374
379
  name: 'close-menu-cross',
375
- colour: Colour['$ui-blue-9']
380
+ colour: Colour['$ui-blue-9'],
376
381
  }}
377
382
  />
378
383
  {/if}
@@ -384,7 +389,7 @@
384
389
  size={Size.Small}
385
390
  icon={{
386
391
  name: isExpanded ? 'chevron-up' : 'chevron-down',
387
- colour: Colour['$ui-blue-9']
392
+ colour: Colour['$ui-blue-9'],
388
393
  }}
389
394
  />
390
395
  </div>
@@ -24,7 +24,6 @@ export interface QuerySelectPropsAsObject<V extends object, VP extends ValueProp
24
24
  export interface QuerySelectPropsAsPrimitive<V extends object, VP extends ValueProp<V>> extends QuerySelectPropsAsObjectCommonProps<V, VP> {
25
25
  valueAsObject: false;
26
26
  value: V[VP] | null;
27
- options: Array<V>;
28
27
  }
29
28
  export type QuerySelectProps<V extends object, VP extends ValueProp<V>> = QuerySelectPropsAsObject<V, VP> | QuerySelectPropsAsPrimitive<V, VP>;
30
29
  import type { NonNullableKey, ValueProp } from '../Types/Internal/Misc.js';
@@ -56,9 +56,15 @@ export declare abstract class ColumnDef<T extends object, in FilterFnType = neve
56
56
  get filterValueType(): FilterValueType | undefined;
57
57
  set filterValueType(v: FilterValueType | undefined);
58
58
  readonly getDisplayValue: ((item: T, index: number) => string | null | Promise<string | null>) | undefined;
59
- toJSON(): object;
59
+ toJSON(): ColumnJSONRepresentation;
60
60
  readonly isEditable: (item: T, index: number) => Promise<boolean>;
61
61
  readonly resizable: boolean;
62
62
  readonly header: string | Snippet;
63
63
  readonly getOptions?: () => Array<SelectOption<OptionsType>> | Promise<Array<SelectOption<OptionsType>>>;
64
64
  }
65
+ export interface ColumnJSONRepresentation {
66
+ columnType: ColumnType;
67
+ id: string;
68
+ header: string;
69
+ filterValueType: FilterValueType | undefined;
70
+ }
@@ -77,7 +77,12 @@ export class ColumnDef {
77
77
  }
78
78
  getDisplayValue;
79
79
  toJSON() {
80
- return { columnType: this.columnType, id: this.id, header: this.header };
80
+ return {
81
+ columnType: this.columnType,
82
+ id: this.id,
83
+ header: this.header.toString(),
84
+ filterValueType: this.filterValueType,
85
+ };
81
86
  }
82
87
  isEditable;
83
88
  resizable;
@@ -1,11 +1,11 @@
1
1
  import type { SortingState } from '../../DataRepository/SortingState.js';
2
2
  import type { JSONColumnPinningState } from '../JSONColumnPinningState.js';
3
3
  import type { VisibilityState } from '../VisibilityState.js';
4
- import type { ColumnDef } from './ColumnDef.svelte.js';
5
- export interface JSONColumnDefSet<T extends object> {
4
+ import type { ColumnJSONRepresentation } from './ColumnDef.svelte.js';
5
+ export interface JSONColumnDefSet {
6
6
  name: string;
7
7
  sortingState: SortingState;
8
8
  visibilityState: VisibilityState;
9
9
  columnPinningState: JSONColumnPinningState;
10
- columnDefs: Array<ColumnDef<T>>;
10
+ columnDefs: Array<ColumnJSONRepresentation>;
11
11
  }
@@ -1,6 +1,9 @@
1
1
  import type { TableCell } from '../Columns/TableCell.js';
2
2
  export interface Row<T extends object> {
3
- getValue: (columnId: string) => Promise<unknown>;
3
+ getValue: {
4
+ <K extends keyof T>(columnId: K): T[K] | Promise<T[K]>;
5
+ (columnId: string): unknown | Promise<unknown>;
6
+ };
4
7
  original: T;
5
8
  visibleIndex: number;
6
9
  originalIndex: number;
@@ -1,5 +1,5 @@
1
1
  import { browser, dev } from '$app/environment';
2
- import { asyncAll, asyncForEach, asyncMap, devCatch, getCanvasContext, } from '../../../../Helpers/Helpers.svelte.js';
2
+ import { asyncAll, asyncForEach, asyncMap, devCatch, getCanvasContext, isKeyOf, } from '../../../../Helpers/Helpers.svelte.js';
3
3
  import { tick, untrack } from 'svelte';
4
4
  import { SvelteMap } from 'svelte/reactivity';
5
5
  import { ColumnType } from '../Columns/ColumnType.js';
@@ -345,6 +345,9 @@ export class TableContext {
345
345
  if (presets[0] && areDefsEquivalent(defs, presets[0].columnDefs)) {
346
346
  return this.reassignDefsToPresets(presets).then((p) => p);
347
347
  }
348
+ else if (dev && presets[0]) {
349
+ console.warn(`invalid presets:`, presets);
350
+ }
348
351
  }
349
352
  }
350
353
  return null;
@@ -1959,6 +1962,9 @@ export class TableContext {
1959
1962
  const jsonFriendly = newPresets.map((preset) => {
1960
1963
  return {
1961
1964
  ...preset,
1965
+ columnDefs: preset.columnDefs.map((it) => {
1966
+ return { ...it.toJSON(), header: it.header.toString() };
1967
+ }),
1962
1968
  columnPinningState: {
1963
1969
  left: [...(preset.columnPinningState.left?.entries() ?? [])],
1964
1970
  right: [...(preset.columnPinningState.right?.entries() ?? [])],
@@ -1981,12 +1987,15 @@ export class TableContext {
1981
1987
  // #region private methods
1982
1988
  createRow(item, index, unsortedIndex) {
1983
1989
  const row = {
1984
- getValue: async (key) => {
1985
- const gotValue = await this.columnDefsById.get(key)?.accessorFn?.(item, unsortedIndex);
1990
+ getValue: (key) => {
1991
+ const gotValue = this.columnDefsById.get(key)?.accessorFn?.(item, unsortedIndex);
1986
1992
  if (gotValue !== undefined) {
1987
1993
  return gotValue;
1988
1994
  }
1989
- return item[key];
1995
+ if (isKeyOf(key, item)) {
1996
+ return item[key];
1997
+ }
1998
+ throw new Error(`Row had neither an accessor function or an id that can index the item object`);
1990
1999
  },
1991
2000
  original: item,
1992
2001
  visibleIndex: index,
@@ -2519,14 +2528,14 @@ export class TableContext {
2519
2528
  return {
2520
2529
  ...preset,
2521
2530
  columnPinningState: adaptJSONPinningState(preset.columnPinningState),
2522
- columnDefs: preset.columnDefs.map((def, index) => {
2531
+ columnDefs: preset.columnDefs.map((def) => {
2523
2532
  const realDef = def.columnType == ColumnType.RowSelect || def.columnType == ColumnType.Actions
2524
2533
  ? adjustedDefs.find((it) => it.columnType === def.columnType)
2525
2534
  : asMap.get(def.id);
2526
2535
  if (!realDef) {
2527
2536
  throw new Error('Could not find equivalent column def:', { cause: def });
2528
2537
  }
2529
- return (preset.columnDefs[index] = realDef);
2538
+ return realDef;
2530
2539
  }),
2531
2540
  };
2532
2541
  });
@@ -211,16 +211,16 @@ export class LocalTableDataRepository extends TableDataRepository {
211
211
  numberFilter = async (row, columnId, // & keyof T,
212
212
  values) => {
213
213
  const columnValue = await row.getValue(columnId);
214
- const filterType = this.filterModes.get(columnId) ?? NumberDateFilterMode.In;
214
+ const mode = this.filterModes.get(columnId) ?? NumberDateFilterMode.In;
215
215
  const isValueInvalid = columnValue == null || typeof columnValue != 'number' || isNaN(columnValue);
216
- if (filterType == NumberDateFilterMode.null) {
216
+ if (mode == NumberDateFilterMode.null) {
217
217
  // TODO test
218
218
  return isValueInvalid;
219
219
  }
220
220
  if (isValueInvalid || typeof columnValue != 'number') {
221
- return inclusiveFilterModes.has(filterType);
221
+ return inclusiveFilterModes.has(mode);
222
222
  }
223
- switch (filterType) {
223
+ switch (mode) {
224
224
  case NumberDateFilterMode.Equals: {
225
225
  const [min] = TableDataRepository.getMinMax(values);
226
226
  return min == undefined || columnValue === min;
@@ -1,6 +1,9 @@
1
1
  <svelte:options runes />
2
2
 
3
- <script lang="ts" generics="Args">
3
+ <script
4
+ lang="ts"
5
+ generics="Args"
6
+ >
4
7
  import { devCatch } from '../Helpers/Helpers.svelte.js';
5
8
  import { Colour } from '../scss/colours.js';
6
9
  import { Size } from '../Types/Internal/Size.js';
@@ -10,6 +13,7 @@
10
13
  let { option, selected }: TabGroupButtonProps<Args> = $props();
11
14
 
12
15
  let disabled = $state(true);
16
+ let visible = $state(true);
13
17
  let isValid = $state(true);
14
18
 
15
19
  $effect(() => {
@@ -36,6 +40,20 @@
36
40
  .catch(devCatch);
37
41
  }
38
42
  });
43
+
44
+ $effect(() => {
45
+ if (typeof option.visible === 'boolean') {
46
+ visible = option.visible;
47
+ } else if (option.visible == undefined) {
48
+ visible = false;
49
+ } else {
50
+ option.visible
51
+ .then((v) => {
52
+ visible = v;
53
+ })
54
+ .catch(devCatch);
55
+ }
56
+ });
39
57
  </script>
40
58
 
41
59
  <button
@@ -47,21 +65,40 @@
47
65
  type="button"
48
66
  class="tab"
49
67
  class:invalid={!isValid}
68
+ class:hidden={!visible}
50
69
  >
51
- <div class="bordered" class:invalid={!isValid}>
70
+ <div
71
+ class="bordered"
72
+ class:invalid={!isValid}
73
+ >
52
74
  {#if option.iconLeft}
53
- <Icon {...option.iconLeft} colour={Colour['$brand-primary']} />
75
+ <Icon
76
+ {...option.iconLeft}
77
+ colour={Colour['$brand-primary']}
78
+ />
54
79
  {/if}
55
80
  {option.label}
56
81
  {#if !isValid}
57
- <Icon name="error" colour={Colour['$error-border']} size={Size.Small} />
82
+ <Icon
83
+ name="error"
84
+ colour={Colour['$error-border']}
85
+ size={Size.Small}
86
+ />
58
87
  {:else if option.iconRight}
59
- <Icon colour={Colour['$brand-primary']} size={Size.Small} {...option.iconRight} />
88
+ <Icon
89
+ colour={Colour['$brand-primary']}
90
+ size={Size.Small}
91
+ {...option.iconRight}
92
+ />
60
93
  {/if}
61
94
  </div>
62
95
  </button>
63
96
 
64
- <style>.tab {
97
+ <style>button.tab.hidden {
98
+ display: none !important;
99
+ }
100
+
101
+ .tab {
65
102
  cursor: pointer;
66
103
  font-size: 0.875rem;
67
104
  font-weight: 600;
@@ -1,3 +1,6 @@
1
+ import type { DefsWrapper } from '../Components/Table/Types/Columns/DefsWrapper.js';
2
+ import { TableContext } from '../Components/Table/Types/Context/TableContext.svelte.js';
3
+ import type { Primitive } from '../Components/Table/Types/Context/TableInitOptions.js';
1
4
  import type { CustomFilter } from '../Components/Table/Types/Filtering/CustomFilter.js';
2
5
  import type { InputDate, InputDateWithTime, SQLDate } from '../Types/Internal/Misc.js';
3
6
  import type { RefWrapper } from '../Types/Internal/RefWrapper.js';
@@ -52,3 +55,12 @@ export declare function isDateTuple(value: unknown): value is [Date | null, Date
52
55
  export declare function isNumericArray(value: unknown): value is Array<number | null>;
53
56
  export declare function isValidityTuple(v: Validity | ValidityWrapper | undefined): v is ValidityWrapper;
54
57
  export declare const loadingText = "loading\u2026";
58
+ export declare function isKeyOf<T extends object>(key: string, obj: T): key is string & keyof T;
59
+ export interface ICreateTableArgs<T extends object, RowIdType extends Primitive> {
60
+ name: string;
61
+ defs: DefsWrapper<T>;
62
+ data: RefWrapper<Array<T>>;
63
+ getUserId: () => string | undefined;
64
+ selectionExtractor: (item: T) => RowIdType | Promise<RowIdType>;
65
+ }
66
+ export declare function createLocalTable<T extends object, RowIdType extends Primitive>(args: ICreateTableArgs<T, RowIdType>): TableContext<T, RowIdType>;
@@ -1,4 +1,6 @@
1
1
  import { dev } from '$app/environment';
2
+ import { TableContext } from '../Components/Table/Types/Context/TableContext.svelte.js';
3
+ import { LocalTableDataRepository } from '../Components/Table/Types/DataRepository/LocalTableDataRepository.svelte.js';
2
4
  import { CustomFilterType } from '../Components/Table/Types/Filtering/CustomFilterType.js';
3
5
  import { Sizing } from '../scss/sizing.js';
4
6
  import { untrack } from 'svelte';
@@ -409,3 +411,13 @@ export function isValidityTuple(v) {
409
411
  return v != null && typeof v == 'object';
410
412
  }
411
413
  export const loadingText = 'loading…';
414
+ export function isKeyOf(key, obj) {
415
+ return key in obj;
416
+ }
417
+ export function createLocalTable(args) {
418
+ const localRepo = new LocalTableDataRepository(args.name, args.data, args.getUserId);
419
+ return TableContext.init(args.defs, args.name, localRepo, {
420
+ selectionExtractor: args.selectionExtractor,
421
+ getUserId: args.getUserId,
422
+ });
423
+ }
@@ -8,5 +8,8 @@ export interface TabOption<Args> {
8
8
  iconLeft?: IconProps;
9
9
  iconRight?: IconProps;
10
10
  isValid?: boolean | Promise<boolean>;
11
+ /** default false */
11
12
  disabled?: boolean | Promise<boolean>;
13
+ /** default true */
14
+ visible?: boolean | Promise<boolean>;
12
15
  }
package/dist/index.d.ts CHANGED
@@ -132,7 +132,7 @@ export { TooltipContext } from './Contexts/TooltipContext.svelte.js';
132
132
  export { UntypedInputContext } from './Contexts/UntypedInputContext.svelte.js';
133
133
  export { Delay } from './Delay/Delay.js';
134
134
  export { DATAMatrix } from './Helpers/Datamatrix.js';
135
- export { asyncAll, asyncEvery, asyncFilter, asyncFilterIndices, asyncFlatMap, asyncForEach, asyncMap, asyncSleep, asyncSome, autoPluralise, devCatch, generateShortUUID, generateUUID, getCanvasContext, getErrorMessage, gridItem, isDateTuple, isNumericArray, isValidEmailAddress, loadingText, localTimeFormat, passthrough, refWrapper, SQLFriendly, SQLFriendlyWithLocalTime, vowels, } from './Helpers/Helpers.svelte.js';
135
+ export { asyncAll, asyncEvery, asyncFilter, asyncFilterIndices, asyncFlatMap, asyncForEach, asyncMap, asyncSleep, asyncSome, autoPluralise, createLocalTable, devCatch, generateShortUUID, generateUUID, getCanvasContext, getErrorMessage, gridItem, isDateTuple, isNumericArray, isValidEmailAddress, loadingText, localTimeFormat, passthrough, refWrapper, SQLFriendly, SQLFriendlyWithLocalTime, vowels, type ICreateTableArgs, } from './Helpers/Helpers.svelte.js';
136
136
  export { type INamed } from './Helpers/INamed.js';
137
137
  export { AttributeOperation } from './Types/Internal/AttributeOperation.js';
138
138
  export { type BigRadioOption } from './Types/Internal/BigRadioOption.js';
package/dist/index.js CHANGED
@@ -132,7 +132,7 @@ export { TooltipContext } from './Contexts/TooltipContext.svelte.js';
132
132
  export { UntypedInputContext } from './Contexts/UntypedInputContext.svelte.js';
133
133
  export { Delay } from './Delay/Delay.js';
134
134
  export { DATAMatrix } from './Helpers/Datamatrix.js';
135
- export { asyncAll, asyncEvery, asyncFilter, asyncFilterIndices, asyncFlatMap, asyncForEach, asyncMap, asyncSleep, asyncSome, autoPluralise, devCatch, generateShortUUID, generateUUID, getCanvasContext, getErrorMessage, gridItem, isDateTuple, isNumericArray, isValidEmailAddress, loadingText, localTimeFormat, passthrough, refWrapper, SQLFriendly, SQLFriendlyWithLocalTime, vowels, } from './Helpers/Helpers.svelte.js';
135
+ export { asyncAll, asyncEvery, asyncFilter, asyncFilterIndices, asyncFlatMap, asyncForEach, asyncMap, asyncSleep, asyncSome, autoPluralise, createLocalTable, devCatch, generateShortUUID, generateUUID, getCanvasContext, getErrorMessage, gridItem, isDateTuple, isNumericArray, isValidEmailAddress, loadingText, localTimeFormat, passthrough, refWrapper, SQLFriendly, SQLFriendlyWithLocalTime, vowels, } from './Helpers/Helpers.svelte.js';
136
136
  export {} from './Helpers/INamed.js';
137
137
  export { AttributeOperation } from './Types/Internal/AttributeOperation.js';
138
138
  export {} from './Types/Internal/BigRadioOption.js';
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.22.5",
4
+ "version": "0.23.0",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },